summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/.editorconfig29
-rw-r--r--modules/mono/Directory.Build.props5
-rw-r--r--modules/mono/Directory.Build.targets22
-rw-r--r--modules/mono/README.md45
-rw-r--r--modules/mono/SCsub35
-rw-r--r--modules/mono/SdkPackageVersions.props5
-rw-r--r--modules/mono/build_scripts/api_solution_build.py80
-rwxr-xr-xmodules/mono/build_scripts/build_assemblies.py329
-rw-r--r--modules/mono/build_scripts/gen_cs_glue_version.py20
-rw-r--r--modules/mono/build_scripts/godot_net_sdk_build.py55
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py38
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py55
-rw-r--r--modules/mono/build_scripts/mono_android_config.xml28
-rw-r--r--modules/mono/build_scripts/mono_configure.py687
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py112
-rw-r--r--modules/mono/build_scripts/solution_builder.py145
-rw-r--r--modules/mono/config.py30
-rw-r--r--modules/mono/csharp_script.cpp2179
-rw-r--r--modules/mono/csharp_script.h188
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml47
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs7
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs109
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs109
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs5
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs31
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs36
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs311
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs228
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj14
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props3
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs148
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs62
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs60
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs73
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs678
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs24
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs100
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs23
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs408
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs36
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs664
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs293
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs19
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs283
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs432
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj26
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe0
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs57
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs224
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs28
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs208
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs99
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs233
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs47
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs388
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs130
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs63
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs214
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs173
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs267
-rw-r--r--modules/mono/editor/bindings_generator.cpp1840
-rw-r--r--modules/mono/editor/bindings_generator.h205
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp330
-rw-r--r--modules/mono/editor/editor_internal_calls.h6
-rw-r--r--modules/mono/editor/godotsharp_export.cpp144
-rw-r--r--modules/mono/editor/godotsharp_export.h48
-rw-r--r--modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs2
-rw-r--r--modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs4
-rw-r--r--modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs2
-rw-r--r--modules/mono/glue/GodotSharp/.editorconfig8
-rw-r--r--modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml5
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs65
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs119
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs6
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj11
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs463
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj17
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/Main.cs270
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs64
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs666
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs25
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs25
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs83
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs18
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs253
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs87
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs89
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs25
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs1028
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs92
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs70
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs292
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs98
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs107
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs243
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs677
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs95
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs220
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs192
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs30
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs154
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs313
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs139
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs1091
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs96
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs1092
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs522
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs99
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs976
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs33
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs611
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs146
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs164
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs135
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs90
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs98
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs85
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs73
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs92
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs120
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs44
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs236
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs19
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs35
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs38
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs37
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj73
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Variant.cs841
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings4
-rw-r--r--modules/mono/glue/arguments_vector.h67
-rw-r--r--modules/mono/glue/base_object_glue.cpp257
-rw-r--r--modules/mono/glue/callable_glue.cpp79
-rw-r--r--modules/mono/glue/collections_glue.cpp374
-rw-r--r--modules/mono/glue/gd_glue.cpp348
-rw-r--r--modules/mono/glue/glue_header.h91
-rw-r--r--modules/mono/glue/nodepath_glue.cpp102
-rw-r--r--modules/mono/glue/rid_glue.cpp64
-rw-r--r--modules/mono/glue/runtime_interop.cpp1513
-rw-r--r--modules/mono/glue/runtime_interop.h (renamed from modules/mono/mono_gd/gd_mono_header.h)28
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp86
-rw-r--r--modules/mono/glue/string_glue.cpp85
-rw-r--r--modules/mono/glue/string_name_glue.cpp62
-rw-r--r--modules/mono/godotsharp_defs.h4
-rw-r--r--modules/mono/godotsharp_dirs.cpp62
-rw-r--r--modules/mono/godotsharp_dirs.h9
-rw-r--r--modules/mono/interop_types.h208
-rw-r--r--modules/mono/managed_callable.cpp60
-rw-r--r--modules/mono/managed_callable.h14
-rw-r--r--modules/mono/mono_gc_handle.cpp32
-rw-r--r--modules/mono/mono_gc_handle.h56
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp1471
-rw-r--r--modules/mono/mono_gd/gd_mono.h246
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp482
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h138
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp350
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h258
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp576
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h160
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp556
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h78
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp145
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h52
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp209
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h71
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp1824
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h605
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp296
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h97
-rw-r--r--modules/mono/mono_gd/gd_mono_method_thunk.h320
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp202
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h80
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp677
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h205
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.cpp79
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.h263
-rw-r--r--modules/mono/mono_gd/i_mono_class_member.h70
-rw-r--r--modules/mono/mono_gd/managed_type.cpp58
-rw-r--r--modules/mono/mono_gd/managed_type.h55
-rw-r--r--modules/mono/mono_gd/support/android_support.cpp2
-rw-r--r--modules/mono/signal_awaiter_utils.cpp82
-rw-r--r--modules/mono/signal_awaiter_utils.h21
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp242
-rw-r--r--modules/mono/utils/mono_reg_utils.h54
-rw-r--r--modules/mono/utils/path_utils.cpp31
-rw-r--r--modules/mono/utils/path_utils.h2
-rw-r--r--modules/mono/utils/string_utils.cpp29
-rw-r--r--modules/mono/utils/string_utils.h3
246 files changed, 21492 insertions, 18829 deletions
diff --git a/modules/mono/.editorconfig b/modules/mono/.editorconfig
index c9dcd7724e..9434d0693c 100644
--- a/modules/mono/.editorconfig
+++ b/modules/mono/.editorconfig
@@ -12,3 +12,32 @@ insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 120
csharp_indent_case_contents_when_block = false
+
+[*.cs]
+# CA1707: Identifiers should not contain underscores
+# TODO:
+# Maybe we could disable this selectively only
+# where it's not desired and for generated code.
+dotnet_diagnostic.CA1707.severity = none
+# CA1711: Identifiers should not have incorrect suffix
+# Disable warning for suffixes like EventHandler, Flags, Enum, etc.
+dotnet_diagnostic.CA1711.severity = none
+# CA1716: Identifiers should not match keywords
+# TODO: We should look into this.
+dotnet_diagnostic.CA1716.severity = warning
+# CA1720: Identifiers should not contain type names
+dotnet_diagnostic.CA1720.severity = none
+# CA1805: Do not initialize unnecessarily
+# Don't tell me what to do.
+dotnet_diagnostic.CA1805.severity = none
+# CA1304: Specify CultureInfo
+# TODO: We should look into this.
+dotnet_diagnostic.CA1304.severity = warning
+# CA1305: Specify IFormatProvider
+# TODO: We should look into this. Disabled for now because it's annoying.
+dotnet_diagnostic.CA1305.severity = none
+# CA1310: Specify StringComparison for correctness
+# TODO: We should look into this. Disabled for now because it's annoying.
+dotnet_diagnostic.CA1310.severity = none
+# Diagnostics to prevent defensive copies of `in` struct parameters
+resharper_possibly_impure_method_call_on_readonly_variable_highlighting = error
diff --git a/modules/mono/Directory.Build.props b/modules/mono/Directory.Build.props
index fbf864b11b..f7c8a825f9 100644
--- a/modules/mono/Directory.Build.props
+++ b/modules/mono/Directory.Build.props
@@ -1,3 +1,6 @@
<Project>
- <Import Project="$(MSBuildThisFileDirectory)\SdkPackageVersions.props" />
+ <PropertyGroup>
+ <GodotSdkPackageVersionsFilePath>$(MSBuildThisFileDirectory)\SdkPackageVersions.props</GodotSdkPackageVersionsFilePath>
+ </PropertyGroup>
+ <Import Project="$(GodotSdkPackageVersionsFilePath)" />
</Project>
diff --git a/modules/mono/Directory.Build.targets b/modules/mono/Directory.Build.targets
new file mode 100644
index 0000000000..98410b93ae
--- /dev/null
+++ b/modules/mono/Directory.Build.targets
@@ -0,0 +1,22 @@
+<Project>
+ <PropertyGroup>
+ <_HasNuGetPackage Condition=" '$(_HasNuGetPackage)' == '' And '$(PackageId)' != '' And '$(GeneratePackageOnBuild.ToLower())' == 'true' ">true</_HasNuGetPackage>
+ <_HasNuGetPackage Condition=" '$(_HasNuGetPackage)' == '' ">false</_HasNuGetPackage>
+ </PropertyGroup>
+ <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack"
+ Condition=" '$(_HasNuGetPackage)' == 'true' ">
+ <PropertyGroup>
+ <GodotSourceRootPath>$(MSBuildThisFileDirectory)\..\..\</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
+ </PropertyGroup>
+ <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
+ </Target>
+ <Target Name="PushNuGetPackagesToLocalSource" BeforeTargets="Pack"
+ Condition=" '$(_HasNuGetPackage)' == 'true' And '$(PushNuGetToLocalSource)' != '' ">
+ <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(PushNuGetToLocalSource)\" />
+ </Target>
+ <Target Name="ClearNuGetLocalPackageCache" BeforeTargets="Pack"
+ Condition=" '$(_HasNuGetPackage)' == 'true' And '$(ClearNuGetLocalCache.ToLower())' == 'true' ">
+ <RemoveDir Directories="$(NugetPackageRoot)/$(PackageId.ToLower())/$(PackageVersion)"/>
+ </Target>
+</Project>
diff --git a/modules/mono/README.md b/modules/mono/README.md
new file mode 100644
index 0000000000..ebbc6b0f80
--- /dev/null
+++ b/modules/mono/README.md
@@ -0,0 +1,45 @@
+# How to build and run
+
+1. Build Godot with the module enabled: `module_mono_enabled=yes`.
+2. After building Godot, use it to generate the C# glue code:
+ ```sh
+ <godot_binary> --generate-mono-glue ./modules/mono/glue
+ ```
+3. Build the C# solutions:
+ ```sh
+ ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir ./bin
+ ```
+
+The paths specified in these examples assume the command is being run from
+the Godot source root.
+
+# How to deal with NuGet packages
+
+We distribute the API assemblies, our source generators, and our custom
+MSBuild project SDK as NuGet packages. This is all transparent to the user,
+but it can make things complicated during development.
+
+In order to use Godot with a development of those packages, we must create
+a local NuGet source where MSBuild can find them. This can be done with
+the .NET CLI:
+
+```sh
+dotnet nuget add source ~/MyLocalNugetSource --name MyLocalNugetSource
+```
+
+The Godot NuGet packages must be added to that local source. Additionally,
+we must make sure there are no other versions of the package in the NuGet
+cache, as MSBuild may pick one of those instead.
+
+In order to simplify this process, the `build_assemblies.py` script provides
+the following `--push-nupkgs-local` option:
+
+```sh
+./modules/mono/build_scripts/build_assemblies.py --godot-output-dir ./bin \
+ --push-nupkgs-local ~/MyLocalNugetSource
+```
+
+This option ensures the packages will be added to the specified local NuGet
+source and that conflicting versions of the package are removed from the
+NuGet cache. It's recommended to always use this option when building the
+C# solutions during development to avoid mistakes.
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index d10ebc7b47..7764ba0b45 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -7,49 +7,14 @@ 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"])
-
# Configure Mono
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")
env_mono.add_source_files(env.modules_sources, "glue/*.cpp")
-env_mono.add_source_files(env.modules_sources, "glue/mono_glue.gen.cpp")
env_mono.add_source_files(env.modules_sources, "mono_gd/*.cpp")
env_mono.add_source_files(env.modules_sources, "utils/*.cpp")
diff --git a/modules/mono/SdkPackageVersions.props b/modules/mono/SdkPackageVersions.props
index bdec051625..65094aa34f 100644
--- a/modules/mono/SdkPackageVersions.props
+++ b/modules/mono/SdkPackageVersions.props
@@ -1,7 +1,8 @@
<Project>
<PropertyGroup>
<PackageFloatingVersion_Godot>4.0.*-*</PackageFloatingVersion_Godot>
- <PackageVersion_Godot_NET_Sdk>4.0.0-dev6</PackageVersion_Godot_NET_Sdk>
- <PackageVersion_Godot_SourceGenerators>4.0.0-dev3</PackageVersion_Godot_SourceGenerators>
+ <PackageVersion_GodotSharp>4.0.0-dev</PackageVersion_GodotSharp>
+ <PackageVersion_Godot_NET_Sdk>4.0.0-dev8</PackageVersion_Godot_NET_Sdk>
+ <PackageVersion_Godot_SourceGenerators>4.0.0-dev8</PackageVersion_Godot_SourceGenerators>
</PropertyGroup>
</Project>
diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py
deleted file mode 100644
index 9abac22df6..0000000000
--- a/modules/mono/build_scripts/api_solution_build.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Build the Godot API solution
-
-import os
-
-from SCons.Script import Dir
-
-
-def build_api_solution(source, target, env):
- # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
-
- module_dir = env["module_dir"]
-
- solution_path = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln")
-
- build_config = env["solution_build_config"]
-
- extra_msbuild_args = ["/p:NoWarn=1591"] # Ignore missing documentation warnings
-
- from .solution_builder import build_solution
-
- build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args)
-
- # Copy targets
-
- core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharp", "bin", build_config))
- editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharpEditor", "bin", build_config))
-
- dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
-
- if not os.path.isdir(dst_dir):
- assert not os.path.isfile(dst_dir)
- os.makedirs(dst_dir)
-
- def copy_target(target_path):
- from shutil import copy
-
- filename = os.path.basename(target_path)
-
- src_path = os.path.join(core_src_dir, filename)
- if not os.path.isfile(src_path):
- src_path = os.path.join(editor_src_dir, filename)
-
- copy(src_path, target_path)
-
- for scons_target in target:
- copy_target(str(scons_target))
-
-
-def build(env_mono):
- assert env_mono["tools"]
-
- target_filenames = [
- "GodotSharp.dll",
- "GodotSharp.pdb",
- "GodotSharp.xml",
- "GodotSharpEditor.dll",
- "GodotSharpEditor.pdb",
- "GodotSharpEditor.xml",
- ]
-
- depend_cmd = []
-
- for build_config in ["Debug", "Release"]:
- output_dir = Dir("#bin").abspath
- editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config)
-
- targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
-
- cmd = env_mono.CommandNoCache(
- targets, depend_cmd, build_api_solution, module_dir=os.getcwd(), solution_build_config=build_config
- )
- env_mono.AlwaysBuild(cmd)
-
- # Make the Release build of the API solution depend on the Debug build.
- # We do this in order to prevent SCons from building them in parallel,
- # which can freak out MSBuild. In many cases, one of the builds would
- # hang indefinitely requiring a key to be pressed for it to continue.
- depend_cmd = cmd
-
- return depend_cmd
diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py
new file mode 100755
index 0000000000..fa3be684bd
--- /dev/null
+++ b/modules/mono/build_scripts/build_assemblies.py
@@ -0,0 +1,329 @@
+#!/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, push_nupkgs_local):
+ target_filenames = [
+ "GodotSharp.dll",
+ "GodotSharp.pdb",
+ "GodotSharp.xml",
+ "GodotSharpEditor.dll",
+ "GodotSharpEditor.pdb",
+ "GodotSharpEditor.xml",
+ "GodotPlugins.dll",
+ "GodotPlugins.pdb",
+ "GodotPlugins.runtimeconfig.json",
+ ]
+
+ 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]
+
+ args = ["/restore", "/t:Build", "/p:Configuration=" + build_config, "/p:NoWarn=1591"]
+ if push_nupkgs_local:
+ args += ["/p:ClearNuGetLocalCache=true", "/p:PushNuGetToLocalSource=" + push_nupkgs_local]
+
+ sln = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln")
+ exit_code = run_msbuild(
+ msbuild_tool,
+ sln=sln,
+ msbuild_args=args,
+ )
+ 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))
+ plugins_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotPlugins", "bin", build_config, "net6.0"))
+
+ 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)
+ if not os.path.isfile(src_path):
+ src_path = os.path.join(plugins_src_dir, filename)
+
+ print(f"Copying assembly to {target_path}...")
+ copy(src_path, target_path)
+
+ for scons_target in targets:
+ copy_target(scons_target)
+
+ return 0
+
+
+def build_all(msbuild_tool, module_dir, output_dir, godot_platform, dev_debug, push_nupkgs_local):
+ # Godot API
+ exit_code = build_godot_api(msbuild_tool, module_dir, output_dir, push_nupkgs_local)
+ 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 []
+ )
+ if push_nupkgs_local:
+ args += ["/p:ClearNuGetLocalCache=true", "/p:PushNuGetToLocalSource=" + push_nupkgs_local]
+ exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args)
+ if exit_code != 0:
+ return exit_code
+
+ # Godot.NET.Sdk
+ args = ["/restore", "/t:Build", "/p:Configuration=Release"]
+ if push_nupkgs_local:
+ args += ["/p:ClearNuGetLocalCache=true", "/p:PushNuGetToLocalSource=" + push_nupkgs_local]
+ sln = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
+ exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args)
+ 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="")
+ parser.add_argument("--push-nupkgs-local", 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,
+ args.push_nupkgs_local,
+ )
+ sys.exit(exit_code)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/modules/mono/build_scripts/gen_cs_glue_version.py b/modules/mono/build_scripts/gen_cs_glue_version.py
deleted file mode 100644
index 98bbb4d9be..0000000000
--- a/modules/mono/build_scripts/gen_cs_glue_version.py
+++ /dev/null
@@ -1,20 +0,0 @@
-def generate_header(solution_dir, version_header_dst):
- import os
-
- latest_mtime = 0
- for root, dirs, files in os.walk(solution_dir, topdown=True):
- dirs[:] = [d for d in dirs if d not in ["Generated"]] # Ignored generated files
- files = [f for f in files if f.endswith(".cs")]
- for file in files:
- filepath = os.path.join(root, file)
- mtime = os.path.getmtime(filepath)
- latest_mtime = mtime if mtime > latest_mtime else latest_mtime
-
- glue_version = int(latest_mtime) # The latest modified time will do for now
-
- with open(version_header_dst, "w") as version_header:
- version_header.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
- version_header.write("#ifndef CS_GLUE_VERSION_H\n")
- version_header.write("#define CS_GLUE_VERSION_H\n\n")
- version_header.write("#define CS_GLUE_VERSION UINT32_C(" + str(glue_version) + ")\n")
- version_header.write("\n#endif // CS_GLUE_VERSION_H\n")
diff --git a/modules/mono/build_scripts/godot_net_sdk_build.py b/modules/mono/build_scripts/godot_net_sdk_build.py
deleted file mode 100644
index 8c5a60d2db..0000000000
--- a/modules/mono/build_scripts/godot_net_sdk_build.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Build Godot.NET.Sdk solution
-
-import os
-
-from SCons.Script import Dir
-
-
-def build_godot_net_sdk(source, target, env):
- # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
-
- module_dir = env["module_dir"]
-
- solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
- build_config = "Release"
-
- from .solution_builder import build_solution
-
- extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
-
- build_solution(env, solution_path, build_config, extra_msbuild_args)
- # No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them.
-
-
-def get_nupkgs_versions(props_file):
- import xml.etree.ElementTree as ET
-
- tree = ET.parse(props_file)
- root = tree.getroot()
-
- return {
- "Godot.NET.Sdk": root.find("./PropertyGroup/PackageVersion_Godot_NET_Sdk").text.strip(),
- "Godot.SourceGenerators": root.find("./PropertyGroup/PackageVersion_Godot_SourceGenerators").text.strip(),
- }
-
-
-def build(env_mono):
- assert env_mono["tools"]
-
- output_dir = Dir("#bin").abspath
- editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
- nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs")
-
- module_dir = os.getcwd()
-
- nupkgs_versions = get_nupkgs_versions(os.path.join(module_dir, "SdkPackageVersions.props"))
-
- target_filenames = [
- "Godot.NET.Sdk.%s.nupkg" % nupkgs_versions["Godot.NET.Sdk"],
- "Godot.SourceGenerators.%s.nupkg" % nupkgs_versions["Godot.SourceGenerators"],
- ]
-
- targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames]
-
- cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir)
- env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
deleted file mode 100644
index 3bbbf29d3b..0000000000
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Build GodotTools solution
-
-import os
-
-from SCons.Script import Dir
-
-
-def build_godot_tools(source, target, env):
- # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
-
- module_dir = env["module_dir"]
-
- solution_path = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln")
- build_config = "Debug" if env["target"] == "debug" else "Release"
-
- from .solution_builder import build_solution
-
- extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
-
- build_solution(env, solution_path, build_config, extra_msbuild_args)
- # No need to copy targets. The GodotTools csproj takes care of copying them.
-
-
-def build(env_mono, api_sln_cmd):
- assert env_mono["tools"]
-
- output_dir = Dir("#bin").abspath
- editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
-
- target_filenames = ["GodotTools.dll"]
-
- if env_mono["target"] == "debug":
- target_filenames += ["GodotTools.pdb"]
-
- targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
-
- cmd = env_mono.CommandNoCache(targets, api_sln_cmd, build_godot_tools, module_dir=os.getcwd())
- env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
deleted file mode 100644
index 3459244bc2..0000000000
--- a/modules/mono/build_scripts/make_android_mono_config.py
+++ /dev/null
@@ -1,55 +0,0 @@
-def generate_compressed_config(config_src, output_dir):
- import os.path
-
- # Source file
- with open(os.path.join(output_dir, "android_mono_config.gen.cpp"), "w") as cpp:
- with open(config_src, "rb") as f:
- buf = f.read()
- decompr_size = len(buf)
- import zlib
-
- # Use maximum zlib compression level to further reduce file size
- # (at the cost of initial build times).
- buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
- compr_size = len(buf)
-
- bytes_seq_str = ""
- for i, buf_idx in enumerate(range(compr_size)):
- if i > 0:
- bytes_seq_str += ", "
- bytes_seq_str += str(buf[buf_idx])
-
- cpp.write(
- """/* THIS FILE IS GENERATED DO NOT EDIT */
-#include "android_mono_config.h"
-
-#ifdef ANDROID_ENABLED
-
-#include "core/io/compression.h"
-
-
-namespace {
-
-// config
-static const int config_compressed_size = %d;
-static const int config_uncompressed_size = %d;
-static const unsigned char config_compressed_data[] = { %s };
-} // namespace
-
-String get_godot_android_mono_config() {
- Vector<uint8_t> data;
- data.resize(config_uncompressed_size);
- uint8_t* w = data.ptrw();
- Compression::decompress(w, config_uncompressed_size, config_compressed_data,
- config_compressed_size, Compression::MODE_DEFLATE);
- String s;
- if (s.parse_utf8((const char *)w, data.size()) != OK) {
- ERR_FAIL_V(String());
- }
- return s;
-}
-
-#endif // ANDROID_ENABLED
-"""
- % (compr_size, decompr_size, bytes_seq_str)
- )
diff --git a/modules/mono/build_scripts/mono_android_config.xml b/modules/mono/build_scripts/mono_android_config.xml
deleted file mode 100644
index e79670afd2..0000000000
--- a/modules/mono/build_scripts/mono_android_config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<configuration>
- <dllmap wordsize="32" dll="i:cygwin1.dll" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="i:cygwin1.dll" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="libc" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="libc" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="intl" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="intl" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="libintl" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="libintl" target="/system/lib64/libc.so" />
- <dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.so" />
- <dllmap dll="System.Native" target="libmono-native.so" />
- <dllmap wordsize="32" dll="i:msvcrt" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="i:msvcrt" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="i:msvcrt.dll" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="i:msvcrt.dll" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="sqlite" target="/system/lib/libsqlite.so" />
- <dllmap wordsize="64" dll="sqlite" target="/system/lib64/libsqlite.so" />
- <dllmap wordsize="32" dll="sqlite3" target="/system/lib/libsqlite.so" />
- <dllmap wordsize="64" dll="sqlite3" target="/system/lib64/libsqlite.so" />
- <dllmap wordsize="32" dll="liblog" target="/system/lib/liblog.so" />
- <dllmap wordsize="64" dll="liblog" target="/system/lib64/liblog.so" />
- <dllmap dll="i:kernel32.dll">
- <dllentry dll="__Internal" name="CopyMemory" target="mono_win32_compat_CopyMemory"/>
- <dllentry dll="__Internal" name="FillMemory" target="mono_win32_compat_FillMemory"/>
- <dllentry dll="__Internal" name="MoveMemory" target="mono_win32_compat_MoveMemory"/>
- <dllentry dll="__Internal" name="ZeroMemory" target="mono_win32_compat_ZeroMemory"/>
- </dllmap>
-</configuration>
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index e69904c54b..8141bc2422 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -1,574 +1,307 @@
import os
import os.path
-import subprocess
-
-from SCons.Script import Dir, Environment
-
-if os.name == "nt":
- from . import mono_reg_utils as monoreg
-
-
-android_arch_dirs = {
- "armv7": "armeabi-v7a",
- "arm64v8": "arm64-v8a",
- "x86": "x86",
- "x86_64": "x86_64",
-}
-
-
-def get_android_out_dir(env):
- return os.path.join(
- Dir("#platform/android/java/lib/libs").abspath,
- "release" if env["target"] == "release" else "debug",
- android_arch_dirs[env["android_arch"]],
- )
-
-
-def find_name_in_dir_files(directory, names, prefixes=[""], extensions=[""]):
- for extension in extensions:
- if extension and not extension.startswith("."):
- extension = "." + extension
- for prefix in prefixes:
- for curname in names:
- if os.path.isfile(os.path.join(directory, prefix + curname + extension)):
- return curname
- return ""
-
-
-def find_file_in_dir(directory, names, prefixes=[""], extensions=[""]):
- for extension in extensions:
- if extension and not extension.startswith("."):
- extension = "." + extension
- for prefix in prefixes:
- for curname in names:
- filename = prefix + curname + extension
- if os.path.isfile(os.path.join(directory, filename)):
- return filename
- return ""
-
-
-def copy_file(src_dir, dst_dir, src_name, dst_name=""):
- from shutil import copy
-
- src_path = os.path.join(Dir(src_dir).abspath, src_name)
- dst_dir = Dir(dst_dir).abspath
-
- if not os.path.isdir(dst_dir):
- os.makedirs(dst_dir)
-
- if dst_name:
- copy(src_path, os.path.join(dst_dir, dst_name))
- else:
- copy(src_path, dst_dir)
def is_desktop(platform):
- return platform in ["windows", "macos", "linuxbsd", "server", "uwp", "haiku"]
+ return platform in ["windows", "macos", "linuxbsd", "uwp", "haiku"]
def is_unix_like(platform):
- return platform in ["macos", "linuxbsd", "server", "android", "haiku", "ios"]
+ return platform in ["macos", "linuxbsd", "android", "haiku", "ios"]
def module_supports_tools_on(platform):
- return platform not in ["android", "javascript", "ios"]
-
-
-def find_wasm_src_dir(mono_root):
- hint_dirs = [
- os.path.join(mono_root, "src"),
- os.path.join(mono_root, "../src"),
- ]
- for hint_dir in hint_dirs:
- if os.path.isfile(os.path.join(hint_dir, "driver.c")):
- return hint_dir
- return ""
+ return is_desktop(platform)
def configure(env, env_mono):
- bits = env["bits"]
- is_android = env["platform"] == "android"
- is_javascript = env["platform"] == "javascript"
- is_ios = env["platform"] == "ios"
- is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"]
+ # is_android = env["platform"] == "android"
+ # is_javascript = env["platform"] == "javascript"
+ # is_ios = env["platform"] == "ios"
+ # is_ios_sim = is_ios and env["arch"] in ["x86_32", "x86_64"]
tools_enabled = env["tools"]
- mono_static = env["mono_static"]
- copy_mono_root = env["copy_mono_root"]
-
- mono_prefix = env["mono_prefix"]
- mono_bcl = env["mono_bcl"]
-
- mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"]
-
- if is_android and not env["android_arch"] in android_arch_dirs:
- raise RuntimeError("This module does not support the specified 'android_arch': " + env["android_arch"])
if tools_enabled and not module_supports_tools_on(env["platform"]):
- # TODO:
- # Android: We have to add the data directory to the apk, concretely the Api and Tools folders.
raise RuntimeError("This module does not currently support building for this platform with tools enabled")
- if is_android and mono_static:
- # FIXME: When static linking and doing something that requires libmono-native, we get a dlopen error as 'libmono-native'
- # seems to depend on 'libmonosgen-2.0'. Could be fixed by re-directing to '__Internal' with a dllmap or in the dlopen hook.
- raise RuntimeError("Statically linking Mono is not currently supported for this platform")
-
- if not mono_static and (is_javascript or is_ios):
- raise RuntimeError("Dynamically linking Mono is not currently supported for this platform")
-
- if not mono_prefix and (os.getenv("MONO32_PREFIX") or os.getenv("MONO64_PREFIX")):
- print(
- "WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the"
- " 'mono_prefix' SCons parameter instead"
- )
-
- # Although we don't support building with tools for any platform where we currently use static AOT,
- # if these are supported in the future, we won't be using static AOT for them as that would be
- # too restrictive for the editor. These builds would probably be made to only use the interpreter.
- mono_aot_static = (is_ios and not is_ios_sim) and not env["tools"]
-
- # Static AOT is only supported on the root domain
- mono_single_appdomain = mono_aot_static
-
- if mono_single_appdomain:
- env_mono.Append(CPPDEFINES=["GD_MONO_SINGLE_APPDOMAIN"])
-
- if (env["tools"] or env["target"] != "release") and not mono_single_appdomain:
+ if env["tools"]:
env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
- if env["platform"] == "windows":
- mono_root = mono_prefix
-
- if not mono_root and os.name == "nt":
- mono_root = monoreg.find_mono_root_dir(bits)
-
- if not mono_root:
- raise RuntimeError(
- "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
- )
+ app_host_dir = find_dotnet_app_host_dir(env)
- print("Found Mono root directory: " + mono_root)
+ def check_app_host_file_exists(file):
+ file_path = os.path.join(app_host_dir, file)
+ if not os.path.isfile(file_path):
+ raise RuntimeError("File not found: " + file_path)
- mono_lib_path = os.path.join(mono_root, "lib")
+ # TODO:
+ # All libnethost does for us is provide a function to find hostfxr.
+ # If we could handle that logic ourselves we could void linking it.
- env.Append(LIBPATH=mono_lib_path)
- env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0"))
+ # nethost file names:
+ # static: libnethost.a/lib
+ # shared: libnethost.a/dylib and nethost.dll
+ check_app_host_file_exists("libnethost.lib" if os.name == "nt" else "libnethost.a")
+ check_app_host_file_exists("nethost.h")
+ check_app_host_file_exists("hostfxr.h")
+ check_app_host_file_exists("coreclr_delegates.h")
- lib_suffixes = [".lib"]
+ env_mono.Prepend(CPPPATH=app_host_dir)
- if not env.msvc:
- # MingW supports both '.a' and '.lib'
- lib_suffixes.insert(0, ".a")
+ env.Append(LIBPATH=[app_host_dir])
- if mono_static:
- if env.msvc:
- mono_static_lib_name = "libmono-static-sgen"
- else:
- mono_static_lib_name = "libmonosgen-2.0"
+ # Only the editor build links nethost, which is needed to find hostfxr.
+ # Exported games don't need this logic as hostfxr is bundled with them.
+ if tools_enabled:
+ libnethost_path = os.path.join(app_host_dir, "libnethost.lib" if os.name == "nt" else "libnethost.a")
- mono_static_lib_file = find_file_in_dir(mono_lib_path, [mono_static_lib_name], extensions=lib_suffixes)
-
- if not mono_static_lib_file:
- raise RuntimeError("Could not find static mono library in: " + mono_lib_path)
+ if env["platform"] == "windows":
+ env_mono.Append(CPPDEFINES=["NETHOST_USE_AS_STATIC"])
if env.msvc:
- env.Append(LINKFLAGS=mono_static_lib_file)
-
- env.Append(LINKFLAGS="Mincore.lib")
- env.Append(LINKFLAGS="msvcrt.lib")
- env.Append(LINKFLAGS="LIBCMT.lib")
- env.Append(LINKFLAGS="Psapi.lib")
+ env.Append(LINKFLAGS="libnethost.lib")
else:
- mono_static_lib_file_path = os.path.join(mono_lib_path, mono_static_lib_file)
- env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_static_lib_file_path, "-Wl,-no-whole-archive"])
-
- env.Append(LIBS=["psapi"])
- env.Append(LIBS=["version"])
+ env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
else:
- mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes)
+ is_apple = env["platform"] in ["macos", "ios"]
+ # is_macos = is_apple and not is_ios
- if not mono_lib_file:
- raise RuntimeError("Could not find mono library in: " + mono_lib_path)
+ # if is_ios and not is_ios_sim:
+ # env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
- if env.msvc:
- env.Append(LINKFLAGS=mono_lib_file)
+ if is_apple:
+ env.Append(LINKFLAGS=["-Wl,-force_load," + libnethost_path])
else:
- mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file)
- env.Append(LINKFLAGS=mono_lib_file_path)
+ env.Append(LINKFLAGS=["-Wl,-whole-archive", libnethost_path, "-Wl,-no-whole-archive"])
- mono_bin_path = os.path.join(mono_root, "bin")
- mono_dll_file = find_file_in_dir(mono_bin_path, mono_lib_names, prefixes=["", "lib"], extensions=[".dll"])
+def find_dotnet_app_host_dir(env):
+ dotnet_version = "6.0"
- if not mono_dll_file:
- raise RuntimeError("Could not find mono shared library in: " + mono_bin_path)
+ dotnet_root = env["dotnet_root"]
- copy_file(mono_bin_path, "#bin", mono_dll_file)
- else:
- is_apple = env["platform"] in ["macos", "ios"]
- is_macos = is_apple and not is_ios
-
- sharedlib_ext = ".dylib" if is_apple else ".so"
-
- mono_root = mono_prefix
- mono_lib_path = ""
- mono_so_file = ""
-
- if not mono_root and (is_android or is_javascript or is_ios):
- raise RuntimeError(
- "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
- )
-
- if not mono_root and is_macos:
- # Try with some known directories under macOS
- hint_dirs = ["/Library/Frameworks/Mono.framework/Versions/Current", "/usr/local/var/homebrew/linked/mono"]
- for hint_dir in hint_dirs:
- if os.path.isdir(hint_dir):
- mono_root = hint_dir
- break
-
- # We can't use pkg-config to link mono statically,
- # but we can still use it to find the mono root directory
- if not mono_root and mono_static:
- mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
- if not mono_root:
- raise RuntimeError(
- "Building with mono_static=yes, but failed to find the mono prefix with pkg-config; "
- + "specify one manually with the 'mono_prefix' SCons parameter"
- )
-
- if is_ios and not is_ios_sim:
- env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
-
- if mono_root:
- print("Found Mono root directory: " + mono_root)
-
- mono_lib_path = os.path.join(mono_root, "lib")
-
- env.Append(LIBPATH=[mono_lib_path])
- env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0"))
-
- mono_lib = find_name_in_dir_files(mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[".a"])
-
- if not mono_lib:
- raise RuntimeError("Could not find mono library in: " + mono_lib_path)
-
- env_mono.Append(CPPDEFINES=["_REENTRANT"])
-
- if mono_static:
- if not is_javascript:
- env.Append(LINKFLAGS=["-rdynamic"])
-
- mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a")
-
- if is_apple:
- if is_macos:
- env.Append(LINKFLAGS=["-Wl,-force_load," + mono_lib_file])
- else:
- arch = env["arch"]
-
- def copy_mono_lib(libname_wo_ext):
- copy_file(
- mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.ios.%s.a" % (libname_wo_ext, arch)
- )
-
- # Copy Mono libraries to the output folder. These are meant to be bundled with
- # the export templates and added to the Xcode project when exporting a game.
- copy_mono_lib("lib" + mono_lib)
- copy_mono_lib("libmono-native")
- copy_mono_lib("libmono-profiler-log")
-
- if not is_ios_sim:
- copy_mono_lib("libmono-ee-interp")
- copy_mono_lib("libmono-icall-table")
- copy_mono_lib("libmono-ilgen")
- else:
- assert is_desktop(env["platform"]) or is_android or is_javascript
- env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_lib_file, "-Wl,-no-whole-archive"])
-
- if is_javascript:
- env.Append(LIBS=["mono-icall-table", "mono-native", "mono-ilgen", "mono-ee-interp"])
-
- wasm_src_dir = os.path.join(mono_root, "src")
- if not os.path.isdir(wasm_src_dir):
- raise RuntimeError("Could not find mono wasm src directory")
-
- # Ideally this should be defined only for 'driver.c', but I can't fight scons for another 2 hours
- env_mono.Append(CPPDEFINES=["CORE_BINDINGS"])
-
- env_mono.add_source_files(
- env.modules_sources,
- [
- os.path.join(wasm_src_dir, "driver.c"),
- os.path.join(wasm_src_dir, "zlib-helper.c"),
- os.path.join(wasm_src_dir, "corebindings.c"),
- ],
- )
-
- env.Append(
- LINKFLAGS=[
- "--js-library",
- os.path.join(wasm_src_dir, "library_mono.js"),
- "--js-library",
- os.path.join(wasm_src_dir, "binding_support.js"),
- "--js-library",
- os.path.join(wasm_src_dir, "dotnet_support.js"),
- ]
- )
- else:
- env.Append(LIBS=[mono_lib])
-
- if is_macos:
- env.Append(LIBS=["iconv", "pthread"])
- elif is_android:
- pass # Nothing
- elif is_ios:
- pass # Nothing, linking is delegated to the exported Xcode project
- elif is_javascript:
- env.Append(LIBS=["m", "rt", "dl", "pthread"])
- else:
- env.Append(LIBS=["m", "rt", "dl", "pthread"])
+ if not dotnet_root:
+ dotnet_cmd = find_dotnet_executable(env["arch"])
+ if dotnet_cmd:
+ sdk_path = find_dotnet_sdk(dotnet_cmd, dotnet_version)
+ if sdk_path:
+ dotnet_root = os.path.abspath(os.path.join(sdk_path, os.pardir))
- if not mono_static:
- mono_so_file = find_file_in_dir(
- mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]
- )
+ if not dotnet_root:
+ raise RuntimeError("Cannot find .NET Core Sdk")
- if not mono_so_file:
- raise RuntimeError("Could not find mono shared library in: " + mono_lib_path)
- else:
- assert not mono_static
+ print("Found .NET Core Sdk root directory: " + dotnet_root)
- # TODO: Add option to force using pkg-config
- print("Mono root directory not found. Using pkg-config instead")
+ dotnet_cmd = os.path.join(dotnet_root, "dotnet.exe" if os.name == "nt" else "dotnet")
- env.ParseConfig("pkg-config monosgen-2 --libs")
- env_mono.ParseConfig("pkg-config monosgen-2 --cflags")
+ runtime_identifier = determine_runtime_identifier(env)
- tmpenv = Environment()
- tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
- tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
+ # TODO: In the future, if it can't be found this way, we want to obtain it
+ # from the runtime.{runtime_identifier}.Microsoft.NETCore.DotNetAppHost NuGet package.
+ app_host_version = find_app_host_version(dotnet_cmd, dotnet_version)
+ if not app_host_version:
+ raise RuntimeError("Cannot find .NET app host for version: " + dotnet_version)
- for hint_dir in tmpenv["LIBPATH"]:
- file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
- if file_found:
- mono_lib_path = hint_dir
- mono_so_file = file_found
- break
+ def get_runtime_path():
+ return os.path.join(
+ dotnet_root,
+ "packs",
+ "Microsoft.NETCore.App.Host." + runtime_identifier,
+ app_host_version,
+ "runtimes",
+ runtime_identifier,
+ "native",
+ )
- if not mono_so_file:
- raise RuntimeError("Could not find mono shared library in: " + str(tmpenv["LIBPATH"]))
+ app_host_dir = get_runtime_path()
+
+ # Some Linux distros use their distro name as the RID in these paths.
+ # If the initial generic path doesn't exist, try to get the RID from `dotnet --info`.
+ # The generic RID should still be the first choice. Some platforms like Windows 10
+ # define the RID as `win10-x64` but still use the generic `win-x64` for directory names.
+ if not app_host_dir or not os.path.isdir(app_host_dir):
+ runtime_identifier = find_dotnet_cli_rid(dotnet_cmd)
+ app_host_dir = get_runtime_path()
+
+ return app_host_dir
+
+
+def determine_runtime_identifier(env):
+ # The keys are Godot's names, the values are the Microsoft's names.
+ # List: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
+ names_map = {
+ "windows": "win",
+ "macos": "osx",
+ "linuxbsd": "linux",
+ }
+ arch_map = {
+ "x86_64": "x64",
+ "x86_32": "x86",
+ "arm64": "arm64",
+ "arm32": "arm",
+ }
+ platform = env["platform"]
+ if is_desktop(platform):
+ return "%s-%s" % (names_map[platform], arch_map[env["arch"]])
+ else:
+ raise NotImplementedError()
- if not mono_static:
- libs_output_dir = get_android_out_dir(env) if is_android else "#bin"
- copy_file(mono_lib_path, libs_output_dir, mono_so_file)
- if not tools_enabled:
- if is_desktop(env["platform"]):
- if not mono_root:
- mono_root = (
- subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
- )
+def find_app_host_version(dotnet_cmd, search_version_str):
+ import subprocess
+ from distutils.version import LooseVersion
- make_template_dir(env, mono_root)
- elif is_android:
- # Compress Android Mono Config
- from . import make_android_mono_config
+ search_version = LooseVersion(search_version_str)
- module_dir = os.getcwd()
- config_file_path = os.path.join(module_dir, "build_scripts", "mono_android_config.xml")
- make_android_mono_config.generate_compressed_config(config_file_path, "mono_gd/")
+ try:
+ env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
+ lines = subprocess.check_output([dotnet_cmd, "--list-runtimes"], env=env).splitlines()
- # Copy the required shared libraries
- copy_mono_shared_libs(env, mono_root, None)
- elif is_javascript:
- pass # No data directory for this platform
- elif is_ios:
- pass # No data directory for this platform
+ for line_bytes in lines:
+ line = line_bytes.decode("utf-8")
+ if not line.startswith("Microsoft.NETCore.App "):
+ continue
- if copy_mono_root:
- if not mono_root:
- mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
+ parts = line.split(" ", 2)
+ if len(parts) < 3:
+ continue
- if tools_enabled:
- # Only supported for editor builds.
- copy_mono_root_files(env, mono_root, mono_bcl)
+ version_str = parts[1]
+ version = LooseVersion(version_str)
-def make_template_dir(env, mono_root):
- from shutil import rmtree
+ if version >= search_version:
+ return version_str
+ except (subprocess.CalledProcessError, OSError) as e:
+ import sys
- platform = env["platform"]
- target = env["target"]
+ print(e, file=sys.stderr)
- template_dir_name = ""
+ return ""
- assert is_desktop(platform)
- template_dir_name = "data.mono.%s.%s.%s" % (platform, env["bits"], target)
+def find_dotnet_arch(dotnet_cmd):
+ import subprocess
- output_dir = Dir("#bin").abspath
- template_dir = os.path.join(output_dir, template_dir_name)
+ try:
+ env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
+ lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines()
- template_mono_root_dir = os.path.join(template_dir, "Mono")
+ for line_bytes in lines:
+ line = line_bytes.decode("utf-8")
- if os.path.isdir(template_mono_root_dir):
- rmtree(template_mono_root_dir) # Clean first
+ parts = line.split(":", 1)
+ if len(parts) < 2:
+ continue
- # Copy etc/mono/
+ arch_str = parts[0].strip()
+ if arch_str != "Architecture":
+ continue
- template_mono_config_dir = os.path.join(template_mono_root_dir, "etc", "mono")
- copy_mono_etc_dir(mono_root, template_mono_config_dir, platform)
+ arch_value = parts[1].strip()
+ arch_map = {"x64": "x86_64", "x86": "x86_32", "arm64": "arm64", "arm32": "arm32"}
+ return arch_map[arch_value]
+ except (subprocess.CalledProcessError, OSError) as e:
+ import sys
- # Copy the required shared libraries
+ print(e, file=sys.stderr)
+
+ return ""
- copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
+def find_dotnet_sdk(dotnet_cmd, search_version_str):
+ import subprocess
+ from distutils.version import LooseVersion
-def copy_mono_root_files(env, mono_root, mono_bcl):
- from glob import glob
- from shutil import copy
- from shutil import rmtree
+ search_version = LooseVersion(search_version_str)
- if not mono_root:
- raise RuntimeError("Mono installation directory not found")
+ try:
+ env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
+ lines = subprocess.check_output([dotnet_cmd, "--list-sdks"], env=env).splitlines()
- output_dir = Dir("#bin").abspath
- editor_mono_root_dir = os.path.join(output_dir, "GodotSharp", "Mono")
+ for line_bytes in lines:
+ line = line_bytes.decode("utf-8")
- if os.path.isdir(editor_mono_root_dir):
- rmtree(editor_mono_root_dir) # Clean first
+ parts = line.split(" ", 1)
+ if len(parts) < 2:
+ continue
- # Copy etc/mono/
+ version_str = parts[0]
- editor_mono_config_dir = os.path.join(editor_mono_root_dir, "etc", "mono")
- copy_mono_etc_dir(mono_root, editor_mono_config_dir, env["platform"])
+ version = LooseVersion(version_str)
- # Copy the required shared libraries
+ if version < search_version:
+ continue
- copy_mono_shared_libs(env, mono_root, editor_mono_root_dir)
+ path_part = parts[1]
+ return path_part[1 : path_part.find("]")]
+ except (subprocess.CalledProcessError, OSError) as e:
+ import sys
- # Copy framework assemblies
+ print(e, file=sys.stderr)
- mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5")
- mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades")
+ return ""
- editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5")
- editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, "Facades")
- if not os.path.isdir(editor_mono_framework_dir):
- os.makedirs(editor_mono_framework_dir)
- if not os.path.isdir(editor_mono_framework_facades_dir):
- os.makedirs(editor_mono_framework_facades_dir)
+def find_dotnet_cli_rid(dotnet_cmd):
+ import subprocess
- for assembly in glob(os.path.join(mono_framework_dir, "*.dll")):
- copy(assembly, editor_mono_framework_dir)
- for assembly in glob(os.path.join(mono_framework_facades_dir, "*.dll")):
- copy(assembly, editor_mono_framework_facades_dir)
+ try:
+ env = dict(os.environ, DOTNET_CLI_UI_LANGUAGE="en-US")
+ lines = subprocess.check_output([dotnet_cmd, "--info"], env=env).splitlines()
+ for line_bytes in lines:
+ line = line_bytes.decode("utf-8")
+ if not line.startswith(" RID:"):
+ continue
-def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
- from distutils.dir_util import copy_tree
- from glob import glob
- from shutil import copy
+ parts = line.split()
+ if len(parts) < 2:
+ continue
- if not os.path.isdir(target_mono_config_dir):
- os.makedirs(target_mono_config_dir)
+ return parts[1]
+ except (subprocess.CalledProcessError, OSError) as e:
+ import sys
- mono_etc_dir = os.path.join(mono_root, "etc", "mono")
- if not os.path.isdir(mono_etc_dir):
- mono_etc_dir = ""
- etc_hint_dirs = []
- if platform != "windows":
- etc_hint_dirs += ["/etc/mono", "/usr/local/etc/mono"]
- if "MONO_CFG_DIR" in os.environ:
- etc_hint_dirs += [os.path.join(os.environ["MONO_CFG_DIR"], "mono")]
- for etc_hint_dir in etc_hint_dirs:
- if os.path.isdir(etc_hint_dir):
- mono_etc_dir = etc_hint_dir
- break
- if not mono_etc_dir:
- raise RuntimeError("Mono installation etc directory not found")
+ print(e, file=sys.stderr)
- copy_tree(os.path.join(mono_etc_dir, "2.0"), os.path.join(target_mono_config_dir, "2.0"))
- copy_tree(os.path.join(mono_etc_dir, "4.0"), os.path.join(target_mono_config_dir, "4.0"))
- copy_tree(os.path.join(mono_etc_dir, "4.5"), os.path.join(target_mono_config_dir, "4.5"))
- if os.path.isdir(os.path.join(mono_etc_dir, "mconfig")):
- copy_tree(os.path.join(mono_etc_dir, "mconfig"), os.path.join(target_mono_config_dir, "mconfig"))
+ return ""
- for file in glob(os.path.join(mono_etc_dir, "*")):
- if os.path.isfile(file):
- copy(file, target_mono_config_dir)
+ENV_PATH_SEP = ";" if os.name == "nt" else ":"
-def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
- from shutil import copy
- def copy_if_exists(src, dst):
- if os.path.isfile(src):
- copy(src, dst)
+def find_dotnet_executable(arch):
+ is_windows = os.name == "nt"
+ windows_exts = os.environ["PATHEXT"].split(ENV_PATH_SEP) if is_windows else None
+ path_dirs = os.environ["PATH"].split(ENV_PATH_SEP)
- platform = env["platform"]
+ search_dirs = path_dirs + [os.getcwd()] # cwd is last in the list
- if platform == "windows":
- src_mono_bin_dir = os.path.join(mono_root, "bin")
- target_mono_bin_dir = os.path.join(target_mono_root_dir, "bin")
+ for dir in path_dirs:
+ search_dirs += [
+ os.path.join(dir, "x64"),
+ os.path.join(dir, "x86"),
+ os.path.join(dir, "arm64"),
+ os.path.join(dir, "arm32"),
+ ] # search subfolders for cross compiling
- if not os.path.isdir(target_mono_bin_dir):
- os.makedirs(target_mono_bin_dir)
+ for dir in search_dirs:
+ path = os.path.join(dir, "dotnet")
- mono_posix_helper_file = find_file_in_dir(
- src_mono_bin_dir, ["MonoPosixHelper"], prefixes=["", "lib"], extensions=[".dll"]
- )
- copy(
- os.path.join(src_mono_bin_dir, mono_posix_helper_file),
- os.path.join(target_mono_bin_dir, "MonoPosixHelper.dll"),
- )
+ if is_windows:
+ for extension in windows_exts:
+ path_with_ext = path + extension
- # For newer versions
- btls_dll_path = os.path.join(src_mono_bin_dir, "libmono-btls-shared.dll")
- if os.path.isfile(btls_dll_path):
- copy(btls_dll_path, target_mono_bin_dir)
- else:
- target_mono_lib_dir = (
- get_android_out_dir(env) if platform == "android" else os.path.join(target_mono_root_dir, "lib")
- )
+ if os.path.isfile(path_with_ext) and os.access(path_with_ext, os.X_OK):
+ sdk_arch = find_dotnet_arch(path_with_ext)
+ if sdk_arch == arch or arch == "":
+ return path_with_ext
+ else:
+ if os.path.isfile(path) and os.access(path, os.X_OK):
+ sdk_arch = find_dotnet_arch(path)
+ if sdk_arch == arch or arch == "":
+ return path
- if not os.path.isdir(target_mono_lib_dir):
- os.makedirs(target_mono_lib_dir)
-
- lib_file_names = []
- if platform == "macos":
- lib_file_names = [
- lib_name + ".dylib"
- for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"]
- ]
- elif is_unix_like(platform):
- lib_file_names = [
- lib_name + ".so"
- for lib_name in [
- "libmono-btls-shared",
- "libmono-ee-interp",
- "libmono-native",
- "libMonoPosixHelper",
- "libmono-profiler-aot",
- "libmono-profiler-coverage",
- "libmono-profiler-log",
- "libMonoSupportW",
- ]
- ]
-
- for lib_file_name in lib_file_names:
- copy_if_exists(os.path.join(mono_root, "lib", lib_file_name), target_mono_lib_dir)
-
-
-def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
- tmpenv = Environment()
- tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
- tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
- for hint_dir in tmpenv["LIBPATH"]:
- name_found = find_name_in_dir_files(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
- if name_found and os.path.isdir(os.path.join(hint_dir, "..", "include", "mono-2.0")):
- return os.path.join(hint_dir, "..")
return ""
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
deleted file mode 100644
index 43c1ec8f8a..0000000000
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ /dev/null
@@ -1,112 +0,0 @@
-import os
-import platform
-
-if os.name == "nt":
- import winreg
-
-
-def _reg_open_key(key, subkey):
- try:
- return winreg.OpenKey(key, subkey)
- except OSError:
- if platform.architecture()[0] == "32bit":
- bitness_sam = winreg.KEY_WOW64_64KEY
- else:
- bitness_sam = winreg.KEY_WOW64_32KEY
- return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam)
-
-
-def _reg_open_key_bits(key, subkey, bits):
- sam = winreg.KEY_READ
-
- if platform.architecture()[0] == "32bit":
- if bits == "64":
- # Force 32bit process to search in 64bit registry
- sam |= winreg.KEY_WOW64_64KEY
- else:
- if bits == "32":
- # Force 64bit process to search in 32bit registry
- sam |= winreg.KEY_WOW64_32KEY
-
- return winreg.OpenKey(key, subkey, 0, sam)
-
-
-def _find_mono_in_reg(subkey, bits):
- try:
- with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
- value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0]
- return value
- except OSError:
- return None
-
-
-def _find_mono_in_reg_old(subkey, bits):
- try:
- with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
- default_clr = winreg.QueryValueEx(hKey, "DefaultCLR")[0]
- if default_clr:
- return _find_mono_in_reg(subkey + "\\" + default_clr, bits)
- return None
- except OSError:
- return None
-
-
-def find_mono_root_dir(bits):
- root_dir = _find_mono_in_reg(r"SOFTWARE\Mono", bits)
- if root_dir is not None:
- return str(root_dir)
- root_dir = _find_mono_in_reg_old(r"SOFTWARE\Novell\Mono", bits)
- if root_dir is not None:
- return str(root_dir)
- return ""
-
-
-def find_msbuild_tools_path_reg():
- import subprocess
-
- vswhere = os.getenv("PROGRAMFILES(X86)")
- if not vswhere:
- vswhere = os.getenv("PROGRAMFILES")
- vswhere += r"\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: " + e.message)
- except subprocess.CalledProcessError as e:
- print(e.output)
- except OSError as e:
- print(e)
-
- # Try to find 14.0 in the Registry
-
- try:
- subkey = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0"
- with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
- value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0]
- return value
- except OSError:
- return ""
diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py
deleted file mode 100644
index 6a621c3c8b..0000000000
--- a/modules/mono/build_scripts/solution_builder.py
+++ /dev/null
@@ -1,145 +0,0 @@
-import os
-
-
-verbose = False
-
-
-def find_dotnet_cli():
- import os.path
-
- if os.name == "nt":
- for hint_dir in os.environ["PATH"].split(os.pathsep):
- hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, "dotnet")
- if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
- return hint_path
- if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
- return hint_path + ".exe"
- else:
- for hint_dir in os.environ["PATH"].split(os.pathsep):
- hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, "dotnet")
- if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
- return hint_path
-
-
-def find_msbuild_unix():
- import os.path
- import sys
-
- hint_dirs = []
- if sys.platform == "darwin":
- hint_dirs[:0] = [
- "/Library/Frameworks/Mono.framework/Versions/Current/bin",
- "/usr/local/var/homebrew/linked/mono/bin",
- ]
-
- for hint_dir in hint_dirs:
- hint_path = os.path.join(hint_dir, "msbuild")
- if os.path.isfile(hint_path):
- return hint_path
- elif os.path.isfile(hint_path + ".exe"):
- return hint_path + ".exe"
-
- for hint_dir in os.environ["PATH"].split(os.pathsep):
- hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, "msbuild")
- if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
- return hint_path
- if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
- return hint_path + ".exe"
-
- return None
-
-
-def find_msbuild_windows(env):
- from .mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
-
- mono_root = env["mono_prefix"] or find_mono_root_dir(env["bits"])
-
- if not mono_root:
- raise RuntimeError("Cannot find mono root directory")
-
- mono_bin_dir = os.path.join(mono_root, "bin")
- msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat")
-
- msbuild_tools_path = find_msbuild_tools_path_reg()
-
- if msbuild_tools_path:
- return (os.path.join(msbuild_tools_path, "MSBuild.exe"), {})
-
- if os.path.isfile(msbuild_mono):
- # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
- # building with Mono's MSBuild. They must point to the batch files
- # in Mono's bin directory to make sure they are executed with Mono.
- mono_msbuild_env = {
- "CscToolExe": os.path.join(mono_bin_dir, "csc.bat"),
- "VbcToolExe": os.path.join(mono_bin_dir, "vbc.bat"),
- "FscToolExe": os.path.join(mono_bin_dir, "fsharpc.bat"),
- }
- return (msbuild_mono, mono_msbuild_env)
-
- return None
-
-
-def run_command(command, args, env_override=None, name=None):
- def cmd_args_to_str(cmd_args):
- return " ".join([arg if not " " in arg else '"%s"' % arg for arg in cmd_args])
-
- args = [command] + args
-
- if name is None:
- name = os.path.basename(command)
-
- if verbose:
- print("Running '%s': %s" % (name, cmd_args_to_str(args)))
-
- import subprocess
-
- try:
- if env_override is None:
- subprocess.check_call(args)
- else:
- subprocess.check_call(args, env=env_override)
- except subprocess.CalledProcessError as e:
- raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode))
-
-
-def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
- global verbose
- verbose = env["verbose"]
-
- msbuild_env = os.environ.copy()
-
- # Needed when running from Developer Command Prompt for VS
- if "PLATFORM" in msbuild_env:
- del msbuild_env["PLATFORM"]
-
- msbuild_args = []
-
- dotnet_cli = find_dotnet_cli()
-
- if dotnet_cli:
- msbuild_path = dotnet_cli
- msbuild_args += ["msbuild"] # `dotnet msbuild` command
- else:
- # Find MSBuild
- if os.name == "nt":
- msbuild_info = find_msbuild_windows(env)
- if msbuild_info is None:
- raise RuntimeError("Cannot find MSBuild executable")
- msbuild_path = msbuild_info[0]
- msbuild_env.update(msbuild_info[1])
- else:
- msbuild_path = find_msbuild_unix()
- if msbuild_path is None:
- raise RuntimeError("Cannot find MSBuild executable")
-
- print("MSBuild path: " + msbuild_path)
-
- # Build solution
-
- msbuild_args += [solution_path, "/restore", "/t:Build", "/p:Configuration=" + build_config]
- msbuild_args += extra_msbuild_args
-
- run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name="msbuild")
diff --git a/modules/mono/config.py b/modules/mono/config.py
index d895d2d92d..b599414a2c 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,4 +1,6 @@
-supported_platforms = ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"]
+# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "haiku", "javascript", "ios"]
+# Eventually support for each them should be added back (except Haiku if not supported by .NET Core)
+supported_platforms = ["windows", "macos", "linuxbsd"]
def can_build(env, platform):
@@ -13,26 +15,11 @@ def get_opts(platform):
return [
PathVariable(
- "mono_prefix",
- "Path to the Mono installation directory for the target platform and architecture",
+ "dotnet_root",
+ "Path to the .NET Sdk installation directory for the target platform and architecture",
"",
PathVariable.PathAccept,
),
- PathVariable(
- "mono_bcl",
- "Path to a custom Mono BCL (Base Class Library) directory for the target 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
- ),
- BoolVariable(
- "mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib
- ),
]
@@ -44,13 +31,6 @@ def configure(env):
env.add_module_version_string("mono")
- if env["mono_bundles_zlib"]:
- # Mono may come with zlib bundled for WASM or on newer version when built with MinGW.
- print("This Mono runtime comes with zlib bundled. Disabling 'builtin_zlib'...")
- env["builtin_zlib"] = False
- thirdparty_zlib_dir = "#thirdparty/zlib/"
- env.Prepend(CPPPATH=[thirdparty_zlib_dir])
-
def get_doc_classes():
return [
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 475b483d6c..8b135051c5 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -30,8 +30,6 @@
#include "csharp_script.h"
-#include <mono/metadata/threads.h>
-#include <mono/metadata/tokentype.h>
#include <stdint.h>
#include "core/config/project_settings.h"
@@ -57,10 +55,8 @@
#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"
-#include "mono_gd/gd_mono_utils.h"
#include "signal_awaiter_utils.h"
#include "utils/macros.h"
#include "utils/string_utils.h"
@@ -69,17 +65,8 @@
#ifdef TOOLS_ENABLED
static bool _create_project_solution_if_needed() {
- String sln_path = GodotSharpDirs::get_project_sln_path();
- String csproj_path = GodotSharpDirs::get_project_csproj_path();
-
- if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
- // A solution does not yet exist, create a new one
-
- CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr);
- return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution");
- }
-
- return true;
+ CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr);
+ return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolutionIfNeeded");
}
#endif
@@ -118,26 +105,21 @@ void CSharpLanguage::init() {
}
#endif
- gdmono = memnew(GDMono);
- gdmono->initialize();
-
#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
- // Generate bindings here, before loading assemblies. 'initialize_load_assemblies' aborts
- // the applications if the api assemblies or the main tools assembly is missing, but this
- // is not a problem for BindingsGenerator as it only needs the tools project editor assembly.
+ // Generate the bindings here, before loading assemblies. The Godot assemblies
+ // may be missing if the glue wasn't generated yet in order to build them.
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
BindingsGenerator::handle_cmdline_args(cmdline_args);
#endif
-#ifndef MONO_GLUE_ENABLED
- print_line("Run this binary with '--generate-mono-glue path/to/modules/mono/glue'");
-#endif
+ gdmono = memnew(GDMono);
+ gdmono->initialize();
+#ifdef TOOLS_ENABLED
if (gdmono->is_runtime_initialized()) {
gdmono->initialize_load_assemblies();
}
-#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&_editor_init_callback);
#endif
}
@@ -596,23 +578,19 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
return Vector<StackInfo>();
}
_recursion_flag_ = true;
- SCOPE_EXIT { _recursion_flag_ = false; };
-
- GD_MONO_SCOPE_THREAD_ATTACH;
+ SCOPE_EXIT {
+ _recursion_flag_ = false;
+ };
- if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated) {
+ if (!gdmono->is_runtime_initialized()) {
return Vector<StackInfo>();
}
- MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
-
- MonoBoolean need_file_info = true;
- void *ctor_args[1] = { &need_file_info };
-
- CACHED_METHOD(System_Diagnostics_StackTrace, ctor_bool)->invoke_raw(stack_trace, ctor_args);
-
Vector<StackInfo> si;
- si = stack_trace_get_info(stack_trace);
+
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.DebuggingUtils_GetCurrentStackInfo(&si);
+ }
return si;
#else
@@ -620,63 +598,6 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#endif
}
-#ifdef DEBUG_ENABLED
-Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
- // Printing an error here will result in endless recursion, so we must be careful
- static thread_local bool _recursion_flag_ = false;
- if (_recursion_flag_) {
- return Vector<StackInfo>();
- }
- _recursion_flag_ = true;
- SCOPE_EXIT { _recursion_flag_ = false; };
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoException *exc = nullptr;
-
- MonoArray *frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames).invoke(p_stack_trace, &exc);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- return Vector<StackInfo>();
- }
-
- int frame_count = mono_array_length(frames);
-
- if (frame_count <= 0) {
- return Vector<StackInfo>();
- }
-
- Vector<StackInfo> si;
- si.resize(frame_count);
-
- for (int i = 0; i < frame_count; i++) {
- StackInfo &sif = si.write[i];
- MonoObject *frame = mono_array_get(frames, MonoObject *, i);
-
- MonoString *file_name;
- int file_line_num;
- MonoString *method_decl;
- CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo).invoke(frame, &file_name, &file_line_num, &method_decl, &exc);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- return Vector<StackInfo>();
- }
-
- // TODO
- // what if the StackFrame method is null (method_decl is empty). should we skip this frame?
- // can reproduce with a MissingMethodException on internal calls
-
- sif.file = GDMonoMarshal::mono_string_to_godot(file_name);
- sif.line = file_line_num;
- sif.func = GDMonoMarshal::mono_string_to_godot(method_decl);
- }
-
- return si;
-}
-#endif
-
void CSharpLanguage::post_unsafe_reference(Object *p_obj) {
#ifdef DEBUG_ENABLED
MutexLock lock(unsafe_object_references_lock);
@@ -698,48 +619,36 @@ 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<MonoGCHandleRef> &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle;
-
- 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 (gdmono && gdmono->is_runtime_initialized() && GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_FrameCallback();
}
}
struct CSharpScriptDepSort {
- // must support sorting so inheritance works properly (parent must be reloaded first)
+ // Must support sorting so inheritance works properly (parent must be reloaded first)
bool operator()(const Ref<CSharpScript> &A, const Ref<CSharpScript> &B) const {
if (A == B) {
- return false; // shouldn't happen but..
+ // Shouldn't happen but just in case...
+ return false;
}
- GDMonoClass *I = B->base;
+ const Script *I = B->get_base_script().ptr();
while (I) {
- if (I == A->script_class) {
+ if (I == A.ptr()) {
// A is a base of B
return true;
}
- I = I->get_parent_class();
+ I = I->get_base_script().ptr();
}
- return false; // not a base
+ // A isn't a base of B
+ return false;
}
};
void CSharpLanguage::reload_all_scripts() {
#ifdef GD_MONO_HOT_RELOAD
if (is_assembly_reloading_needed()) {
- GD_MONO_SCOPE_THREAD_ATTACH;
reload_assemblies(false);
}
#endif
@@ -756,7 +665,6 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
#ifdef GD_MONO_HOT_RELOAD
if (is_assembly_reloading_needed()) {
- GD_MONO_SCOPE_THREAD_ATTACH;
reload_assemblies(p_soft_reload);
}
#endif
@@ -768,28 +676,28 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
return false;
}
- GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
-
- String appname_safe = ProjectSettings::get_singleton()->get_safe_project_name();
-
- appname_safe += ".dll";
+ String assembly_path = gdmono->get_project_assembly_path();
- if (proj_assembly) {
- String proj_asm_path = proj_assembly->get_path();
-
- if (!FileAccess::exists(proj_asm_path)) {
- // Maybe it wasn't loaded from the default path, so check this as well
- proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe);
- if (!FileAccess::exists(proj_asm_path)) {
- return false; // No assembly to load
- }
+ if (!assembly_path.is_empty()) {
+ if (!FileAccess::exists(assembly_path)) {
+ return false; // No assembly to load
}
- if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) {
+ if (FileAccess::get_modified_time(assembly_path) <= gdmono->get_project_assembly_modified_time()) {
return false; // Already up to date
}
} else {
- if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe))) {
+ String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
+
+ if (assembly_name.is_empty()) {
+ assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
+ }
+
+ assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
+ .plus_file(assembly_name + ".dll");
+ assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
+
+ if (!FileAccess::exists(assembly_path)) {
return false; // No assembly to load
}
}
@@ -802,6 +710,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
return;
}
+ // TODO:
+ // Currently, this reloads all scripts, including those whose class is not part of the
+ // assembly load context being unloaded. As such, we unnecessarily reload GodotTools.
+
+ print_verbose(".NET: Reloading assemblies...");
+
// There is no soft reloading with Mono. It's always hard reloading.
List<Ref<CSharpScript>> scripts;
@@ -824,18 +738,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
ManagedCallable *managed_callable = elem->self();
- MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle.get_target();
+ ERR_CONTINUE(managed_callable->delegate_handle.value == nullptr);
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);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
+ bool success = GDMonoCache::managed_callbacks.DelegateUtils_TrySerializeDelegateWithGCHandle(
+ managed_callable->delegate_handle, &serialized_data);
if (success) {
ManagedCallable::instances_pending_reload.insert(managed_callable, serialized_data);
@@ -864,17 +772,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// If someone removes a script from a node, deletes the script, builds, adds a script to the
// same node, then builds again, the script might have no path and also no script_class. In
// that case, we can't (and don't need to) reload it.
- if (script->get_path().is_empty() && !script->script_class) {
+ if (script->get_path().is_empty() && !script->valid) {
continue;
}
to_reload.push_back(script);
- if (script->get_path().is_empty()) {
- script->tied_class_name_for_reload = script->script_class->get_name_for_lookup();
- script->tied_class_namespace_for_reload = script->script_class->get_namespace();
- }
-
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
@@ -907,17 +810,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
- // Call OnBeforeSerialize
- if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call(string_names.on_before_serialize);
- }
+ // Call OnBeforeSerialize and save instance info
- // Save instance info
CSharpScript::StateBackup state;
- // TODO: Proper state backup (Not only variants, serialize managed state of scripts)
- csi->get_properties_state_for_reloading(state.properties);
- csi->get_event_signals_state_for_reloading(state.event_signals);
+ Dictionary properties;
+
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_SerializeState(
+ csi->get_gchandle_intptr(), &properties, &state.event_signals);
+
+ for (const Variant *s = properties.next(nullptr); s != nullptr; s = properties.next(s)) {
+ StringName name = *s;
+ Variant value = properties[*s];
+ state.properties.push_back(Pair<StringName, Variant>(name, value));
+ }
owners_map[obj->get_instance_id()] = state;
}
@@ -934,7 +840,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
// Do domain reload
- if (gdmono->reload_scripts_domain() != OK) {
+ if (gdmono->reload_project_assemblies() != OK) {
// Failed to reload the scripts domain
// Make sure to add the scripts back to their owners before returning
for (Ref<CSharpScript> &scr : to_reload) {
@@ -965,6 +871,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
scr->pending_reload_state.erase(obj_id);
}
+
+ scr->pending_reload_instances.clear();
+ scr->pending_reload_state.clear();
}
return;
@@ -976,53 +885,27 @@ 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);
if (!script->valid) {
script->pending_reload_instances.clear();
+ script->pending_reload_state.clear();
continue;
}
} else {
- const StringName &class_namespace = script->tied_class_namespace_for_reload;
- const StringName &class_name = script->tied_class_name_for_reload;
- GDMonoAssembly *project_assembly = gdmono->get_project_assembly();
-
- // Search in project and tools assemblies first as those are the most likely to have the class
- GDMonoClass *script_class = (project_assembly ? project_assembly->get_class(class_namespace, class_name) : nullptr);
-
-#ifdef TOOLS_ENABLED
- if (!script_class) {
- GDMonoAssembly *tools_assembly = gdmono->get_tools_assembly();
- script_class = (tools_assembly ? tools_assembly->get_class(class_namespace, class_name) : nullptr);
- }
-#endif
+ bool success = GDMonoCache::managed_callbacks.ScriptManagerBridge_TryReloadRegisteredScriptWithClass(script.ptr());
- if (!script_class) {
- script_class = gdmono->get_class(class_namespace, class_name);
- }
-
- if (!script_class) {
- // The class was removed, can't reload
- script->pending_reload_instances.clear();
- continue;
- }
-
- bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(script_class);
- if (!obj_type) {
- // The class no longer inherits Godot.Object, can't reload
+ if (!success) {
+ // Couldn't reload
script->pending_reload_instances.clear();
+ script->pending_reload_state.clear();
continue;
}
-
- GDMonoClass *native = GDMonoUtils::get_class_native_base(script_class);
-
- CSharpScript::initialize_for_managed_type(script, script_class, native);
}
- StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native);
+ StringName native_name = script->get_instance_base_type();
{
for (const ObjectID &obj_id : script->pending_reload_instances) {
@@ -1087,57 +970,25 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
ERR_CONTINUE(!obj->get_script_instance());
- // TODO: Restore serialized state
-
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
- for (const Pair<StringName, Variant> &G : state_backup.properties) {
- obj->get_script_instance()->set(G.first, G.second);
- }
-
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
if (csi) {
- for (const Pair<StringName, Array> &G : state_backup.event_signals) {
- const StringName &name = G.first;
- const Array &serialized_data = G.second;
-
- HashMap<StringName, CSharpScript::EventSignal>::Iterator match = script->event_signals.find(name);
+ Dictionary properties;
- if (!match) {
- // The event or its signal attribute were removed
- continue;
- }
-
- const CSharpScript::EventSignal &event_signal = 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);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
-
- if (success) {
- ERR_CONTINUE(delegate == nullptr);
- 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");
- }
+ for (const Pair<StringName, Variant> &G : state_backup.properties) {
+ properties[G.first] = G.second;
}
- // Call OnAfterDeserialization
- if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call(string_names.on_after_deserialize);
- }
+ // Restore serialized state and call OnAfterDeserialization
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_DeserializeState(
+ csi->get_gchandle_intptr(), &properties, &state_backup.event_signals);
}
}
script->pending_reload_instances.clear();
+ script->pending_reload_state.clear();
}
// Deserialize managed callables
@@ -1148,20 +999,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
ManagedCallable *managed_callable = elem.key;
const Array &serialized_data = elem.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);
+ GCHandleIntPtr delegate = { nullptr };
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
+ bool success = GDMonoCache::managed_callbacks.DelegateUtils_TryDeserializeDelegateWithGCHandle(
+ &serialized_data, &delegate);
if (success) {
- ERR_CONTINUE(delegate == nullptr);
- managed_callable->set_delegate(delegate);
+ ERR_CONTINUE(delegate.value == nullptr);
+ managed_callable->delegate_handle = delegate;
} else if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Failed to deserialize delegate\n");
}
@@ -1180,60 +1025,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
#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<String> *p_extensions) const {
p_extensions->push_back("cs");
}
@@ -1248,22 +1039,6 @@ bool CSharpLanguage::overrides_external_editor() {
}
#endif
-void CSharpLanguage::thread_enter() {
-#if 0
- if (gdmono->is_runtime_initialized()) {
- GDMonoUtils::attach_current_thread();
- }
-#endif
-}
-
-void CSharpLanguage::thread_exit() {
-#if 0
- if (gdmono->is_runtime_initialized()) {
- GDMonoUtils::detach_current_thread();
- }
-#endif
-}
-
bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
// Not a parser error in our case, but it's still used for other type of errors
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
@@ -1289,49 +1064,35 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
}
}
-void CSharpLanguage::_on_scripts_domain_unloaded() {
- for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
- CSharpScriptBinding &script_binding = E.value;
- script_binding.gchandle.release();
- script_binding.inited = false;
- }
-
+void CSharpLanguage::_on_scripts_domain_about_to_unload() {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(ManagedCallable::instances_mutex);
for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
ManagedCallable *managed_callable = elem->self();
- managed_callable->delegate_handle.release();
- managed_callable->delegate_invoke = nullptr;
+ managed_callable->release_delegate_handle();
}
}
#endif
-
- dotnet_script_lookup_map.clear();
}
#ifdef TOOLS_ENABLED
void CSharpLanguage::_editor_init_callback() {
- register_editor_internal_calls();
-
- // Initialize GodotSharpEditor
-
- GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor");
- CRASH_COND(editor_klass == nullptr);
+ // Load GodotTools and initialize GodotSharpEditor
- MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr());
- CRASH_COND(mono_object == nullptr);
+ int32_t interop_funcs_size = 0;
+ const void **interop_funcs = godotsharp::get_editor_interop_funcs(interop_funcs_size);
- MonoException *exc = nullptr;
- GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc);
- UNHANDLED_EXCEPTION(exc);
+ Object *editor_plugin_obj = GDMono::get_singleton()->get_plugin_callbacks().LoadToolsAssemblyCallback(
+ GodotSharpDirs::get_data_editor_tools_dir().plus_file("GodotTools.dll").utf16(),
+ interop_funcs, interop_funcs_size);
+ CRASH_COND(editor_plugin_obj == nullptr);
- EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(
- GDMonoMarshal::mono_object_to_variant(mono_object).operator Object *());
+ EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(editor_plugin_obj);
CRASH_COND(godotsharp_editor == nullptr);
- // Enable it as a plugin
+ // Add plugin to EditorNode and enable it
EditorNode::add_editor_plugin(godotsharp_editor);
ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B);
godotsharp_editor->enable_plugin();
@@ -1352,24 +1113,24 @@ 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
-
- if (!p_gchandle.is_released()) { // Do not lock unnecessarily
+void CSharpLanguage::release_script_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, MonoGCHandleData &r_gchandle) {
+ if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) { // 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();
+ if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) {
+ r_gchandle.release();
}
}
+}
- GDMonoUtils::free_gchandle(pinned_gchandle);
+void CSharpLanguage::release_binding_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, CSharpScriptBinding &r_script_binding) {
+ MonoGCHandleData &gchandle = r_script_binding.gchandle;
+ if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) { // Do not lock unnecessarily
+ MutexLock lock(get_singleton()->script_gchandle_release_mutex);
+ if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) {
+ gchandle.release();
+ r_script_binding.inited = false; // Here too, to be thread safe
+ }
+ }
}
CSharpLanguage::CSharpLanguage() {
@@ -1401,18 +1162,23 @@ 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);
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!r_script_binding.gchandle.is_released());
+#endif
- MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object);
+ GCHandleIntPtr strong_gchandle =
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding(
+ &type_name, p_object);
- 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
@@ -1455,7 +1221,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;
@@ -1465,8 +1231,6 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there
}
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
{
MutexLock lock(csharp_lang->language_bind_mutex);
@@ -1477,11 +1241,11 @@ 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);
- }
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_SetGodotObjectPtr(
+ script_binding.gchandle.get_intptr(), nullptr);
+
script_binding.gchandle.release();
+ script_binding.inited = false;
}
csharp_lang->script_bindings.erase(data);
@@ -1510,41 +1274,49 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token,
if (p_reference) {
// Refcount incremented
if (refcount > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
// The reference count was increased after the managed side was the only one referencing our owner.
// 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 = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = false;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ 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;
} else {
// Refcount decremented
if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
// 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 = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = true;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ 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;
}
@@ -1589,214 +1361,160 @@ 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
-CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
- CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
-
- RefCounted *rc = Object::cast_to<RefCounted>(p_owner);
+ CRASH_COND(!p_unmanaged);
- instance->base_ref_counted = rc != nullptr;
- instance->owner = p_owner;
- instance->gchandle = p_gchandle;
+ // 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
- if (instance->base_ref_counted) {
- instance->_reference_owner_unsafe();
- }
+ RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
- p_script->instances.insert(p_owner);
+ CRASH_COND(p_ref_counted != (bool)rc);
- return instance;
-}
+ MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr,
+ p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE);
-MonoObject *CSharpInstance::get_mono_object() const {
- ERR_FAIL_COND_V(gchandle.is_released(), nullptr);
- return gchandle.get_target();
-}
+ // 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
-Object *CSharpInstance::get_owner() {
- return owner;
-}
-
-bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
- ERR_FAIL_COND_V(!script.is_valid(), false);
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL_V(mono_object, false);
+ CSharpScriptBinding script_binding;
- GDMonoClass *top = script->script_class;
+ script_binding.inited = true;
+ script_binding.type_name = *p_native_name;
+ script_binding.gchandle = gchandle;
+ script_binding.owner = p_unmanaged;
- while (top && top != script->native) {
- GDMonoField *field = top->get_field(p_name);
+ 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)
- if (field) {
- field->set_value_from_variant(mono_object, p_value);
- return true;
+ // 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);
}
+ }
- GDMonoProperty *property = top->get_property(p_name);
-
- if (property) {
- property->set_value_from_variant(mono_object, p_value);
- return true;
- }
+ // The object was just created, no script instance binding should have been attached
+ CRASH_COND(CSharpLanguage::has_instance_binding(p_unmanaged));
- top = top->get_parent_class();
+ void *data;
+ {
+ MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
+ data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(p_unmanaged, script_binding);
}
- // Call _set
+ // Should be thread safe because the object was just created and nothing else should be referencing it
+ CSharpLanguage::set_instance_binding(p_unmanaged, data);
+}
- top = script->script_class;
+void CSharpLanguage::tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, Ref<CSharpScript> *p_script, bool p_ref_counted) {
+ // This method should not fail
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2);
+ Ref<CSharpScript> script = *p_script;
+ // We take care of destructing this reference here, so the managed code won't need to do another P/Invoke call
+ p_script->~Ref();
- if (method) {
- Variant name = p_name;
- const Variant *args[2] = { &name, &p_value };
+ CRASH_COND(!p_unmanaged);
- MonoObject *ret = method->invoke(mono_object, args);
+ // 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
- if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret)) {
- return true;
- }
+ RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
- break;
- }
+ CRASH_COND(p_ref_counted != (bool)rc);
- top = top->get_parent_class();
- }
+ MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr,
+ p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE);
- return false;
-}
+ CRASH_COND(script.is_null());
-bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
- ERR_FAIL_COND_V(!script.is_valid(), false);
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL_V(mono_object, false);
+ CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle);
- GDMonoClass *top = script->script_class;
+ p_unmanaged->set_script_and_instance(script, csharp_instance);
- while (top && top != script->native) {
- GDMonoField *field = top->get_field(p_name);
+ csharp_instance->connect_event_signals();
+}
- if (field) {
- MonoObject *value = field->get_value(mono_object);
- r_ret = GDMonoMarshal::mono_object_to_variant(value);
- return true;
- }
+void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
+ // This method should not fail
- GDMonoProperty *property = top->get_property(p_name);
+ CRASH_COND(!p_unmanaged);
- 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;
- }
+ CSharpInstance *instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance());
- top = top->get_parent_class();
+ if (!instance) {
+ // Native bindings don't need post-setup
+ return;
}
- // Call _get
+ CRASH_COND(!instance->gchandle.is_released());
- 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;
- }
+ // Tie managed to unmanaged
+ instance->gchandle = MonoGCHandleData(p_gchandle_intptr, gdmono::GCHandleType::STRONG_HANDLE);
- break;
- }
+ if (instance->base_ref_counted) {
+ instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
+ }
- top = top->get_parent_class();
+ {
+ 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);
}
- return false;
+ instance->connect_event_signals();
}
-void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state) {
- List<PropertyInfo> property_list;
- get_property_list(&property_list);
-
- for (const PropertyInfo &prop_info : property_list) {
- Pair<StringName, Variant> state_pair;
- state_pair.first = prop_info.name;
+CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
+ CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
- ManagedType managedType;
+ RefCounted *rc = Object::cast_to<RefCounted>(p_owner);
- GDMonoField *field = nullptr;
- GDMonoClass *top = script->script_class;
- while (top && top != script->native) {
- field = top->get_field(state_pair.first);
- if (field) {
- break;
- }
+ instance->base_ref_counted = rc != nullptr;
+ instance->owner = p_owner;
+ instance->gchandle = p_gchandle;
- top = top->get_parent_class();
- }
- if (!field) {
- continue; // Properties ignored. We get the property baking fields instead.
- }
+ if (instance->base_ref_counted) {
+ instance->_reference_owner_unsafe();
+ }
- managedType = field->get_type();
+ p_script->instances.insert(p_owner);
- if (GDMonoMarshal::managed_to_variant_type(managedType) != Variant::NIL) { // If we can marshal it
- if (get(state_pair.first, state_pair.second)) {
- r_state.push_back(state_pair);
- }
- }
- }
+ return instance;
}
-void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state) {
- MonoObject *owner_managed = get_mono_object();
- ERR_FAIL_NULL(owner_managed);
+Object *CSharpInstance::get_owner() {
+ return owner;
+}
- for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
- const CSharpScript::EventSignal &event_signal = E.value;
+bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
- if (!delegate_field_value) {
- continue; // Empty
- }
+ return GDMonoCache::managed_callbacks.CSharpInstanceBridge_Set(
+ gchandle.get_intptr(), &p_name, &p_value);
+}
- Array serialized_data;
- MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- MonoException *exc = nullptr;
- bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate_field_value, managed_serialized_data, &exc);
+ Variant ret_value;
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
+ bool ret = GDMonoCache::managed_callbacks.CSharpInstanceBridge_Get(
+ gchandle.get_intptr(), &p_name, &ret_value);
- if (success) {
- r_state.push_back(Pair<StringName, Array>(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");
- }
+ if (ret) {
+ r_ret = ret_value;
+ return true;
}
+
+ return false;
}
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
@@ -1807,30 +1525,25 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
ERR_FAIL_COND(!script.is_valid());
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL(mono_object);
-
- GDMonoClass *top = script->script_class;
+ StringName method = SNAME("_get_property_list");
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0);
+ Variant ret;
+ Callable::CallError call_error;
+ bool ok = GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &method, nullptr, 0, &call_error, &ret);
- if (method) {
- MonoObject *ret = method->invoke(mono_object);
-
- if (ret) {
- Array array = Array(GDMonoMarshal::mono_object_to_variant(ret));
- for (int i = 0, size = array.size(); i < size; i++) {
- props.push_back(PropertyInfo::from_dict(array.get(i)));
- }
+ // CALL_ERROR_INVALID_METHOD would simply mean it was not overridden
+ if (call_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (call_error.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT("Error calling '_get_property_list': " + Variant::get_call_error_text(method, nullptr, 0, call_error));
+ } else if (!ok) {
+ ERR_PRINT("Unexpected error calling '_get_property_list'");
+ } else {
+ Array array = ret;
+ for (int i = 0, size = array.size(); i < size; i++) {
+ p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
}
-
- break;
}
-
- top = top->get_parent_class();
}
for (const PropertyInfo &prop : props) {
@@ -1853,84 +1566,79 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *
return Variant::NIL;
}
-void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
- if (!script->is_valid() || !script->script_class) {
- return;
- }
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
- GDMonoClass *top = script->script_class;
+bool CSharpInstance::property_can_revert(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- while (top && top != script->native) {
- const Vector<GDMonoMethod *> &methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- MethodInfo minfo = methods[i]->get_method_info();
- if (minfo.name != CACHED_STRING_NAME(dotctor)) {
- p_list->push_back(minfo);
- }
- }
+ Variant name_arg = p_name;
+ const Variant *args[1] = { &name_arg };
- top = top->get_parent_class();
- }
-}
+ Variant ret;
+ Callable::CallError call_error;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &CACHED_STRING_NAME(_property_can_revert), args, 1, &call_error, &ret);
-bool CSharpInstance::has_method(const StringName &p_method) const {
- if (!script.is_valid()) {
+ if (call_error.error != Callable::CallError::CALL_OK) {
return false;
}
- GD_MONO_SCOPE_THREAD_ATTACH;
+ return (bool)ret;
+}
- GDMonoClass *top = script->script_class;
+bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- while (top && top != script->native) {
- if (top->has_fetched_method_unknown_params(p_method)) {
- return true;
- }
+ Variant name_arg = p_name;
+ const Variant *args[1] = { &name_arg };
+
+ Variant ret;
+ Callable::CallError call_error;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &CACHED_STRING_NAME(_property_get_revert), args, 1, &call_error, &ret);
- top = top->get_parent_class();
+ if (call_error.error != Callable::CallError::CALL_OK) {
+ return false;
}
- return false;
+ r_ret = ret;
+ return true;
}
-Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- ERR_FAIL_COND_V(!script.is_valid(), Variant());
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoObject *mono_object = get_mono_object();
-
- if (!mono_object) {
- r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- ERR_FAIL_V(Variant());
+void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
+ if (!script->is_valid() || !script->valid) {
+ return;
}
- GDMonoClass *top = script->script_class;
+ const CSharpScript *top = script.ptr();
+ while (top != nullptr) {
+ for (const CSharpScript::CSharpMethodInfo &E : top->methods) {
+ p_list->push_back(E.method_info);
+ }
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(p_method, p_argcount);
+ top = top->base_script.ptr();
+ }
+}
- if (method) {
- MonoObject *return_value = method->invoke(mono_object, p_args);
+bool CSharpInstance::has_method(const StringName &p_method) const {
+ if (!script.is_valid()) {
+ return false;
+ }
- r_error.error = Callable::CallError::CALL_OK;
+ if (!GDMonoCache::godot_api_cache_updated) {
+ return false;
+ }
- if (return_value) {
- return GDMonoMarshal::mono_object_to_variant(return_value);
- } else {
- return Variant();
- }
- }
+ return GDMonoCache::managed_callbacks.CSharpInstanceBridge_HasMethodUnknownParams(
+ gchandle.get_intptr(), &p_method);
+}
- top = top->get_parent_class();
- }
+Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ ERR_FAIL_COND_V(!script.is_valid(), Variant());
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ Variant ret;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &p_method, p_args, p_argcount, &r_error, &ret);
- return Variant();
+ return ret;
}
bool CSharpInstance::_reference_owner_unsafe() {
@@ -1976,48 +1684,29 @@ bool CSharpInstance::_unreference_owner_unsafe() {
return static_cast<RefCounted *>(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());
+ bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance(
+ script.ptr(), owner, nullptr, 0);
- if (!mono_object) {
+ if (!ok) {
// Important to clear this before destroying the script instance here
script = Ref<CSharpScript>();
-
- 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.");
- }
-
- // Tie managed to unmanaged
- gchandle = MonoGCHandleData::new_strong_handle(mono_object);
-
- if (base_ref_counted) {
- _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
+ return false;
}
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner);
-
- // Construct
- ctor->invoke_raw(mono_object, nullptr);
+ CRASH_COND(gchandle.is_released());
- return mono_object;
+ return true;
}
-void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
+void CSharpInstance::mono_object_disposed(GCHandleIntPtr p_gchandle_to_free) {
// Must make sure event signals are not left dangling
disconnect_event_signals();
@@ -2025,10 +1714,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_thread_safe(p_gchandle_to_free, 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(GCHandleIntPtr p_gchandle_to_free, 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());
@@ -2044,20 +1733,20 @@ 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_thread_safe(p_gchandle_to_free, gchandle);
if (!p_is_finalizer) {
// If the native instance is still alive and Dispose() was called
// (instead of the finalizer), then we remove the script instance.
r_remove_script_instance = true;
+ // TODO: Last usage of 'is_finalizing_scripts_domain'. It should be replaced with a check to determine if the load context is being unloaded.
} else if (!GDMono::get_singleton()->is_finalizing_scripts_domain()) {
// If the native instance is still alive and this is called from the finalizer,
// then it was referenced from another thread before the finalizer could
// unreference and delete it, so we want to keep it.
// GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this'
// could have already been collected. Instead we will create a new managed instance here.
- MonoObject *new_managed = _internal_new_managed();
- if (!new_managed) {
+ if (!_internal_new_managed()) {
r_remove_script_instance = true;
}
}
@@ -2065,17 +1754,20 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
}
void CSharpInstance::connect_event_signals() {
- for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
- const CSharpScript::EventSignal &event_signal = E.value;
+ CSharpScript *top = script.ptr();
+ while (top != nullptr) {
+ for (CSharpScript::EventSignalInfo &signal : top->get_script_event_signals()) {
+ String signal_name = signal.name;
- StringName signal_name = event_signal.field->get_name();
+ // TODO: Use pooling for ManagedCallable instances.
+ EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, signal_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(signal_name, callable);
+ top = top->base_script.ptr();
}
}
@@ -2097,16 +1789,25 @@ void CSharpInstance::refcount_incremented() {
RefCounted *rc_owner = Object::cast_to<RefCounted>(owner);
if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
// The reference count was increased after the managed side was the only one referencing our owner.
// 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.
// 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 = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = false;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ if (!target_alive) {
+ return; // Called after the managed side was collected, so nothing to do here
+ }
+
+ gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
}
}
@@ -2121,15 +1822,24 @@ bool CSharpInstance::refcount_decremented() {
int refcount = rc_owner->reference_get_count();
if (refcount == 1 && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
// If owner owner is no longer referenced by the unmanaged side,
// 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 = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = true;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ 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;
}
@@ -2144,8 +1854,6 @@ const Variant CSharpInstance::get_rpc_config() const {
}
void CSharpInstance::notification(int p_notification) {
- GD_MONO_SCOPE_THREAD_ATTACH;
-
if (p_notification == Object::NOTIFICATION_PREDELETE) {
// When NOTIFICATION_PREDELETE is sent, we also take the chance to call Dispose().
// It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed
@@ -2165,15 +1873,8 @@ 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);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- }
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose(
+ gchandle.get_intptr(), /* okIfNull */ false);
return;
}
@@ -2182,62 +1883,29 @@ 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);
-
- // Custom version of _call_multilevel, optimized for _notification
+ Variant arg = p_notification;
+ const Variant *args[1] = { &arg };
+ StringName method_name = SNAME("_notification");
- int32_t arg = p_notification;
- void *args[1] = { &arg };
- StringName method_name = CACHED_STRING_NAME(_notification);
+ Callable::CallError call_error;
- 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();
- }
+ Variant ret;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &method_name, args, 1, &call_error, &ret);
}
String CSharpInstance::to_string(bool *r_valid) {
- GD_MONO_SCOPE_THREAD_ATTACH;
+ String res;
+ bool valid;
- MonoObject *mono_object = get_mono_object();
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallToString(
+ gchandle.get_intptr(), &res, &valid);
- if (mono_object == nullptr) {
- if (r_valid) {
- *r_valid = false;
- }
- return String();
- }
-
- MonoException *exc = nullptr;
- MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- if (r_valid) {
- *r_valid = false;
- }
- 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<Script> CSharpInstance::get_script() const {
@@ -2253,8 +1921,6 @@ CSharpInstance::CSharpInstance(const Ref<CSharpScript> &p_script) :
}
CSharpInstance::~CSharpInstance() {
- GD_MONO_SCOPE_THREAD_ATTACH;
-
destructing_script_instance = true;
// Must make sure event signals are not left dangling
@@ -2268,16 +1934,8 @@ CSharpInstance::~CSharpInstance() {
// we must call Dispose here, because Dispose calls owner->set_script_instance(nullptr)
// and that would mess up with the new script instance if called later.
- MonoObject *mono_object = gchandle.get_target();
-
- if (mono_object) {
- MonoException *exc = nullptr;
- GDMonoUtils::dispose(mono_object, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose(
+ gchandle.get_intptr(), /* okIfNull */ true);
}
gchandle.release(); // Make sure the gchandle is released
@@ -2341,63 +1999,8 @@ void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values,
propnames.push_back(prop_info);
}
- if (base_cache.is_valid()) {
- base_cache->_update_exports_values(values, propnames);
- }
-}
-
-void CSharpScript::_update_member_info_no_exports() {
- if (exports_invalidated) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- exports_invalidated = false;
-
- member_info.clear();
-
- GDMonoClass *top = script_class;
- List<PropertyInfo> props;
-
- while (top && top != native) {
- PropertyInfo prop_info;
- bool exported;
-
- const Vector<GDMonoField *> &fields = top->get_all_fields();
-
- for (int i = fields.size() - 1; i >= 0; i--) {
- GDMonoField *field = fields[i];
-
- if (_get_member_export(field, /* inspect export: */ false, prop_info, exported)) {
- StringName member_name = field->get_name();
-
- member_info[member_name] = prop_info;
- props.push_front(prop_info);
- exported_members_defval_cache[member_name] = Variant();
- }
- }
-
- const Vector<GDMonoProperty *> &properties = top->get_all_properties();
-
- for (int i = properties.size() - 1; i >= 0; i--) {
- GDMonoProperty *property = properties[i];
-
- if (_get_member_export(property, /* inspect export: */ false, prop_info, exported)) {
- StringName member_name = property->get_name();
-
- member_info[member_name] = prop_info;
- props.push_front(prop_info);
- exported_members_defval_cache[member_name] = Variant();
- }
- }
-
- exported_members_cache.push_back(PropertyInfo(Variant::NIL, top->get_name(), PROPERTY_HINT_NONE, get_path(), PROPERTY_USAGE_CATEGORY));
- for (const PropertyInfo &E : props) {
- exported_members_cache.push_back(E);
- }
-
- props.clear();
-
- top = top->get_parent_class();
- }
+ if (base_script.is_valid()) {
+ base_script->_update_exports_values(values, propnames);
}
}
#endif
@@ -2419,166 +2022,65 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
if (exports_invalidated)
#endif
{
- GD_MONO_SCOPE_THREAD_ATTACH;
+#ifdef TOOLS_ENABLED
+ exports_invalidated = false;
+#endif
changed = true;
member_info.clear();
#ifdef TOOLS_ENABLED
- MonoObject *tmp_object = nullptr;
- Object *tmp_native = nullptr;
- uint32_t tmp_pinned_gchandle = 0;
-
- if (is_editor) {
- exports_invalidated = false;
-
- exported_members_cache.clear();
- exported_members_defval_cache.clear();
-
- // Here we create a temporary managed instance of the class to get the initial values
- tmp_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
-
- if (!tmp_object) {
- ERR_PRINT("Failed to allocate temporary MonoObject.");
- return false;
- }
-
- tmp_pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(tmp_object); // pin it (not sure if needed)
-
- GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
-
- ERR_FAIL_NULL_V_MSG(ctor, false,
- "Cannot construct temporary MonoObject because the class does not define a parameterless constructor: '" + get_path() + "'.");
-
- MonoException *ctor_exc = nullptr;
- ctor->invoke(tmp_object, nullptr, &ctor_exc);
-
- tmp_native = GDMonoMarshal::unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(tmp_object));
-
- if (ctor_exc) {
- // TODO: Should we free 'tmp_native' if the exception was thrown after its creation?
-
- GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
- tmp_object = nullptr;
-
- ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
- GDMonoUtils::debug_print_unhandled_exception(ctor_exc);
- return false;
- }
- }
+ exported_members_cache.clear();
+ exported_members_defval_cache.clear();
#endif
- GDMonoClass *top = script_class;
- List<PropertyInfo> props;
-
- while (top && top != native) {
- PropertyInfo prop_info;
- bool exported;
-
- const Vector<GDMonoField *> &fields = top->get_all_fields();
-
- for (int i = fields.size() - 1; i >= 0; i--) {
- GDMonoField *field = fields[i];
-
- if (_get_member_export(field, /* inspect export: */ true, prop_info, exported)) {
- StringName member_name = field->get_name();
-
- member_info[member_name] = prop_info;
-
- if (exported) {
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyInfoList(this,
+ [](CSharpScript *p_script, const String *p_current_class_name, GDMonoCache::godotsharp_property_info *p_props, int32_t p_count) {
#ifdef TOOLS_ENABLED
- if (is_editor) {
- props.push_front(prop_info);
-
- if (tmp_object) {
- exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
- }
- }
+ p_script->exported_members_cache.push_back(PropertyInfo(
+ Variant::NIL, *p_current_class_name, PROPERTY_HINT_NONE,
+ p_script->get_path(), PROPERTY_USAGE_CATEGORY));
#endif
-#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
- exported_members_names.insert(member_name);
-#endif
- }
- }
- }
+ for (int i = 0; i < p_count; i++) {
+ const GDMonoCache::godotsharp_property_info &prop = p_props[i];
- const Vector<GDMonoProperty *> &properties = top->get_all_properties();
+ StringName name = *reinterpret_cast<const StringName *>(&prop.name);
+ String hint_string = *reinterpret_cast<const String *>(&prop.hint_string);
- for (int i = properties.size() - 1; i >= 0; i--) {
- GDMonoProperty *property = properties[i];
+ PropertyInfo pinfo(prop.type, name, prop.hint, hint_string, prop.usage);
- if (_get_member_export(property, /* inspect export: */ true, prop_info, exported)) {
- StringName member_name = property->get_name();
+ p_script->member_info[name] = pinfo;
- member_info[member_name] = prop_info;
+ if (prop.exported) {
- if (exported) {
#ifdef TOOLS_ENABLED
- if (is_editor) {
- props.push_front(prop_info);
- if (tmp_object) {
- MonoException *exc = nullptr;
- MonoObject *ret = property->get_value(tmp_object, &exc);
- if (exc) {
- exported_members_defval_cache[member_name] = Variant();
- GDMonoUtils::debug_print_unhandled_exception(exc);
- } else {
- exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(ret);
- }
- }
- }
+ p_script->exported_members_cache.push_back(pinfo);
#endif
#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
- exported_members_names.insert(member_name);
+ p_script->exported_members_names.insert(name);
#endif
- }
- }
- }
-
-#ifdef TOOLS_ENABLED
- exported_members_cache.push_back(PropertyInfo(Variant::NIL, top->get_name(), PROPERTY_HINT_NONE, get_path(), PROPERTY_USAGE_CATEGORY));
-
- for (const PropertyInfo &E : props) {
- exported_members_cache.push_back(E);
- }
-
- props.clear();
-#endif // TOOLS_ENABLED
-
- top = top->get_parent_class();
- }
+ }
+ }
+ });
#ifdef TOOLS_ENABLED
- if (is_editor) {
- // Need to check this here, before disposal
- bool base_ref_counted = Object::cast_to<RefCounted>(tmp_native) != nullptr;
-
- // Dispose the temporary managed instance
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyDefaultValues(this,
+ [](CSharpScript *p_script, GDMonoCache::godotsharp_property_def_val_pair *p_def_vals, int32_t p_count) {
+ for (int i = 0; i < p_count; i++) {
+ const GDMonoCache::godotsharp_property_def_val_pair &def_val_pair = p_def_vals[i];
- MonoException *exc = nullptr;
- GDMonoUtils::dispose(tmp_object, &exc);
+ StringName name = *reinterpret_cast<const StringName *>(&def_val_pair.name);
+ Variant value = *reinterpret_cast<const Variant *>(&def_val_pair.value);
- if (exc) {
- ERR_PRINT("Exception thrown from method Dispose() of temporary MonoObject:");
- GDMonoUtils::debug_print_unhandled_exception(exc);
- }
-
- GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
- tmp_object = nullptr;
-
- if (tmp_native && !base_ref_counted) {
- Node *node = Object::cast_to<Node>(tmp_native);
- if (node && node->is_inside_tree()) {
- ERR_PRINT("Temporary instance was added to the scene tree.");
- } else {
- memdelete(tmp_native);
- }
- }
- }
+ p_script->exported_members_defval_cache[name] = value;
+ }
+ });
#endif
+ }
}
#ifdef TOOLS_ENABLED
@@ -2605,374 +2107,6 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
return changed;
}
-void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) {
- // no need to load the script's signals more than once
- if (!signals_invalidated) {
- return;
- }
-
- // make sure this classes signals are empty when loading for the first time
- _signals.clear();
- event_signals.clear();
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- GDMonoClass *top = p_class;
- while (top && top != p_native_class) {
- const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
- for (int i = delegates.size() - 1; i >= 0; --i) {
- GDMonoClass *delegate = delegates[i];
-
- if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
- continue;
- }
-
- // Arguments are accessibles as arguments of .Invoke method
- GDMonoMethod *invoke_method = delegate->get_method(mono_get_delegate_invoke(delegate->get_mono_ptr()));
-
- Vector<SignalParameter> parameters;
- if (_get_signal(top, invoke_method, parameters)) {
- _signals[delegate->get_name()] = parameters;
- }
- }
-
- List<StringName> found_event_signals;
-
- void *iter = nullptr;
- MonoEvent *raw_event = nullptr;
- while ((raw_event = mono_class_get_events(top->get_mono_ptr(), &iter)) != nullptr) {
- MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event);
- if (event_attrs) {
- if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) {
- String event_name = String::utf8(mono_event_get_name(raw_event));
- found_event_signals.push_back(StringName(event_name));
- }
-
- mono_custom_attrs_free(event_attrs);
- }
- }
-
- const Vector<GDMonoField *> &fields = top->get_all_fields();
- for (int i = 0; i < fields.size(); i++) {
- GDMonoField *field = fields[i];
-
- GDMonoClass *field_class = field->get_type().type_class;
-
- if (!mono_class_is_delegate(field_class->get_mono_ptr())) {
- continue;
- }
-
- if (!found_event_signals.find(field->get_name())) {
- continue;
- }
-
- GDMonoMethod *invoke_method = field_class->get_method(mono_get_delegate_invoke(field_class->get_mono_ptr()));
-
- Vector<SignalParameter> parameters;
- if (_get_signal(top, invoke_method, parameters)) {
- event_signals[field->get_name()] = { field, invoke_method, parameters };
- }
- }
-
- top = top->get_parent_class();
- }
-
- signals_invalidated = false;
-}
-
-bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- Vector<StringName> names;
- Vector<ManagedType> types;
- p_delegate_invoke->get_parameter_names(names);
- p_delegate_invoke->get_parameter_types(types);
-
- for (int i = 0; i < names.size(); ++i) {
- SignalParameter arg;
- arg.name = names[i];
-
- bool nil_is_variant = false;
- arg.type = GDMonoMarshal::managed_to_variant_type(types[i], &nil_is_variant);
-
- if (arg.type == Variant::NIL) {
- if (nil_is_variant) {
- arg.nil_is_variant = true;
- } else {
- ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
- return false;
- }
- }
-
- params.push_back(arg);
- }
-
- return true;
-}
-
-/**
- * Returns false if there was an error, otherwise true.
- * If there was an error, r_prop_info and r_exported are not assigned any value.
- */
-bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- // Goddammit, C++. All I wanted was some nested functions.
-#define MEMBER_FULL_QUALIFIED_NAME(m_member) \
- (m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name())
-
- if (p_member->is_static()) {
-#ifdef TOOLS_ENABLED
- if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
- ERR_PRINT("Cannot export member because it is static: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
- }
-#endif
- return false;
- }
-
- if (member_info.has(p_member->get_name())) {
- return false;
- }
-
- ManagedType type;
-
- if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_FIELD) {
- type = static_cast<GDMonoField *>(p_member)->get_type();
- } else if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
- type = static_cast<GDMonoProperty *>(p_member)->get_type();
- } else {
- CRASH_NOW();
- }
-
- bool exported = p_member->has_attribute(CACHED_CLASS(ExportAttribute));
-
- if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
- GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
- if (!property->has_getter()) {
-#ifdef TOOLS_ENABLED
- if (exported) {
- ERR_PRINT("Cannot export a property without a getter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
- }
-#endif
- return false;
- }
- if (!property->has_setter()) {
-#ifdef TOOLS_ENABLED
- if (exported) {
- ERR_PRINT("Cannot export a property without a setter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
- }
-#endif
- return false;
- }
- }
-
- bool nil_is_variant = false;
- Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &nil_is_variant);
-
- if (!p_inspect_export || !exported) {
- r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
- r_exported = false;
- return true;
- }
-
-#ifdef TOOLS_ENABLED
- MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
-#endif
-
- PropertyHint hint = PROPERTY_HINT_NONE;
- String hint_string;
-
- if (variant_type == Variant::NIL && !nil_is_variant) {
-#ifdef TOOLS_ENABLED
- ERR_PRINT("Unknown exported member type: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
-#endif
- return false;
- }
-
-#ifdef TOOLS_ENABLED
- int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string);
-
- ERR_FAIL_COND_V_MSG(hint_res == -1, false,
- "Error while trying to determine information about the exported member: '" +
- MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
-
- if (hint_res == 0) {
- hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
- hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
- }
-#endif
-
- uint32_t prop_usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE;
-
- if (variant_type == Variant::NIL) {
- // System.Object (Variant)
- prop_usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
-
- r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, prop_usage);
- r_exported = true;
-
- return true;
-
-#undef MEMBER_FULL_QUALIFIED_NAME
-}
-
-#ifdef TOOLS_ENABLED
-int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
- if (p_variant_type == Variant::NIL) {
- // System.Object (Variant)
- return 1;
- }
-
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
- r_hint = GDMonoUtils::Marshal::type_has_flags_attribute(reftype) ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
-
- Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
-
- MonoType *enum_basetype = mono_class_enum_basetype(p_type.type_class->get_mono_ptr());
-
- String name_only_hint_string;
-
- // True: enum Foo { Bar, Baz, Quux }
- // True: enum Foo { Bar = 0, Baz = 1, Quux = 2 }
- // False: enum Foo { Bar = 0, Baz = 7, Quux = 5 }
- bool uses_default_values = true;
-
- for (int i = 0; i < fields.size(); i++) {
- MonoClassField *field = fields[i];
-
- if (i > 0) {
- r_hint_string += ",";
- name_only_hint_string += ",";
- }
-
- String enum_field_name = String::utf8(mono_field_get_name(field));
- r_hint_string += enum_field_name;
- name_only_hint_string += enum_field_name;
-
- // TODO:
- // Instead of using mono_field_get_value_object, we can do this without boxing. Check the
- // internal mono functions: ves_icall_System_Enum_GetEnumValuesAndNames and the get_enum_field.
-
- MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, nullptr);
-
- ERR_FAIL_NULL_V_MSG(val_obj, -1, "Failed to get '" + enum_field_name + "' constant enum value.");
-
- bool r_error;
- uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
- ERR_FAIL_COND_V_MSG(r_error, -1, "Failed to unbox '" + enum_field_name + "' constant enum value.");
-
- unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i;
- if (val != expected_val) {
- uses_default_values = false;
- }
-
- r_hint_string += ":";
- r_hint_string += String::num_uint64(val);
- }
-
- if (uses_default_values) {
- // If we use the format NAME:VAL, that's what the editor displays.
- // That's annoying if the user is not using custom values for the enum constants.
- // This may not be needed in the future if the editor is changed to not display values.
- r_hint_string = name_only_hint_string;
- }
- } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) {
- GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
- CRASH_COND(field_native_class == nullptr);
-
- r_hint = PROPERTY_HINT_RESOURCE_TYPE;
- r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
- } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(Node)->is_assignable_from(p_type.type_class)) {
- GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
- CRASH_COND(field_native_class == nullptr);
-
- r_hint = PROPERTY_HINT_NODE_TYPE;
- r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
- } else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
- // Nested arrays are not supported in the inspector
-
- ManagedType elem_type;
-
- if (!GDMonoMarshal::try_get_array_element_type(p_type, elem_type)) {
- return 0;
- }
-
- Variant::Type elem_variant_type = GDMonoMarshal::managed_to_variant_type(elem_type);
-
- PropertyHint elem_hint = PROPERTY_HINT_NONE;
- String elem_hint_string;
-
- ERR_FAIL_COND_V_MSG(elem_variant_type == Variant::NIL, -1, "Unknown array element type.");
-
- bool preset_hint = false;
- if (elem_variant_type == Variant::STRING) {
- MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
- if (PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)) == PROPERTY_HINT_ENUM) {
- r_hint_string = itos(elem_variant_type) + "/" + itos(PROPERTY_HINT_ENUM) + ":" + CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
- preset_hint = true;
- }
- }
-
- if (!preset_hint) {
- int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
-
- ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
-
- // Format: type/hint:hint_string
- r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
- }
-
- r_hint = PROPERTY_HINT_TYPE_STRING;
-
- } else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) {
- // TODO: Dictionaries are not supported in the inspector
- } else {
- return 0;
- }
-
- return 1;
-}
-#endif
-
-Variant CSharpScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (unlikely(GDMono::get_singleton() == nullptr)) {
- // Probably not the best error but eh.
- r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- return Variant();
- }
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- GDMonoMethod *method = top->get_method(p_method, p_argcount);
-
- if (method && method->is_static()) {
- MonoObject *result = method->invoke(nullptr, p_args);
-
- if (result) {
- return GDMonoMarshal::mono_object_to_variant(result);
- } else {
- return Variant();
- }
- }
-
- top = top->get_parent_class();
- }
-
- // No static method found. Try regular instance calls
- return Script::callp(p_method, p_args, p_argcount, r_error);
-}
-
-void CSharpScript::_resource_path_changed() {
- _update_name();
-}
-
bool CSharpScript::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == CSharpLanguage::singleton->string_names._script_source) {
r_ret = get_source_code();
@@ -3000,107 +2134,107 @@ void CSharpScript::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &CSharpScript::_new, MethodInfo("new"));
}
-Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native) {
- // This method should not fail, only assertions allowed
-
- CRASH_COND(p_class == nullptr);
+void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
+ // IMPORTANT:
+ // This method must be called only after the CSharpScript and its associated type
+ // have been added to the script bridge map in the ScriptManagerBridge C# class.
+ // Other than that, it's the same as `CSharpScript::reload`.
- // TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
- Ref<CSharpScript> script = memnew(CSharpScript);
+ // This method should not fail, only assertions allowed.
- initialize_for_managed_type(script, p_class, p_native);
-
- return script;
-}
-
-void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native) {
- // This method should not fail, only assertions allowed
-
- CRASH_COND(p_class == nullptr);
-
- p_script->name = p_class->get_name();
- p_script->script_class = p_class;
- p_script->native = p_native;
-
- CRASH_COND(p_script->native == nullptr);
+ // Unlike `reload`, we print an error rather than silently returning,
+ // as we can assert this won't be called a second time until invalidated.
+ ERR_FAIL_COND(!p_script->reload_invalidated);
p_script->valid = true;
p_script->reload_invalidated = false;
update_script_class_info(p_script);
-#ifdef TOOLS_ENABLED
- p_script->_update_member_info_no_exports();
-#endif
+ p_script->_update_exports();
}
// Extract information about the script using the mono class.
void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
- GDMonoClass *base = p_script->script_class->get_parent_class();
+ bool tool = false;
- // `base` should only be set if the script is a user defined type.
- if (base != p_script->native) {
- p_script->base = base;
- }
+ // TODO: Use GDNative godot_dictionary
+ Array methods_array;
+ methods_array.~Array();
+ Dictionary rpc_functions_dict;
+ rpc_functions_dict.~Dictionary();
+ Dictionary signals_dict;
+ signals_dict.~Dictionary();
- p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
+ Ref<CSharpScript> base_script;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
+ p_script.ptr(), &tool, &methods_array, &rpc_functions_dict, &signals_dict, &base_script);
- if (!p_script->tool) {
- GDMonoClass *nesting_class = p_script->script_class->get_nesting_class();
- p_script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
- }
+ p_script->tool = tool;
-#ifdef TOOLS_ENABLED
- if (!p_script->tool) {
- p_script->tool = p_script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
- }
-#endif
+ p_script->rpc_config.clear();
+ p_script->rpc_config = rpc_functions_dict;
-#ifdef DEBUG_ENABLED
- // For debug builds, we must fetch from all native base methods as well.
- // Native base methods must be fetched before the current class.
- // Not needed if the script class itself is a native class.
+ // Methods
- if (p_script->script_class != p_script->native) {
- GDMonoClass *native_top = p_script->native;
- while (native_top) {
- native_top->fetch_methods_with_godot_api_checks(p_script->native);
+ p_script->methods.clear();
- if (native_top == CACHED_CLASS(GodotObject)) {
- break;
- }
+ p_script->methods.resize(methods_array.size());
+ int push_index = 0;
+
+ for (int i = 0; i < methods_array.size(); i++) {
+ Dictionary method_info_dict = methods_array[i];
- native_top = native_top->get_parent_class();
+ StringName name = method_info_dict["name"];
+
+ MethodInfo mi;
+ mi.name = name;
+
+ Array params = method_info_dict["params"];
+
+ for (int j = 0; j < params.size(); j++) {
+ Dictionary param = params[j];
+
+ Variant::Type param_type = (Variant::Type)(int)param["type"];
+ PropertyInfo arg_info = PropertyInfo(param_type, (String)param["name"]);
+ arg_info.usage = (uint32_t)param["usage"];
+ mi.arguments.push_back(arg_info);
}
+
+ p_script->methods.set(push_index++, CSharpMethodInfo{ name, mi });
}
-#endif
- p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native);
+ // Event signals
- p_script->rpc_config.clear();
+ // Performance is not critical here as this will be replaced with source generators.
- GDMonoClass *top = p_script->script_class;
- while (top && top != p_script->native) {
- // Fetch methods from base classes as well
- top->fetch_methods_with_godot_api_checks(p_script->native);
+ p_script->event_signals.clear();
- // Update RPC info
- {
- Vector<GDMonoMethod *> methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); i++) {
- if (!methods[i]->is_static()) {
- const Variant rpc_config = p_script->_member_get_rpc_config(methods[i]);
- if (rpc_config.get_type() != Variant::NIL) {
- p_script->rpc_config[methods[i]->get_name()] = rpc_config;
- }
- }
- }
+ // Sigh... can't we just have capacity?
+ p_script->event_signals.resize(signals_dict.size());
+ push_index = 0;
+
+ for (const Variant *s = signals_dict.next(nullptr); s != nullptr; s = signals_dict.next(s)) {
+ StringName name = *s;
+
+ MethodInfo mi;
+ mi.name = name;
+
+ Array params = signals_dict[*s];
+
+ for (int i = 0; i < params.size(); i++) {
+ Dictionary param = params[i];
+
+ Variant::Type param_type = (Variant::Type)(int)param["type"];
+ PropertyInfo arg_info = PropertyInfo(param_type, (String)param["name"]);
+ arg_info.usage = (uint32_t)param["usage"];
+ mi.arguments.push_back(arg_info);
}
- top = top->get_parent_class();
+ p_script->event_signals.set(push_index++, EventSignalInfo{ name, mi });
}
- p_script->load_script_signals(p_script->script_class, p_script->native);
+ p_script->base_script = base_script;
}
bool CSharpScript::can_instantiate() const {
@@ -3113,43 +2247,22 @@ bool CSharpScript::can_instantiate() const {
// FIXME Need to think this through better.
// For tool scripts, this will never fire if the class is not found. That's because we
// don't know if it's a tool script if we can't find the class to access the attributes.
- if (extra_cond && !script_class) {
- if (GDMono::get_singleton()->get_project_assembly() == nullptr) {
- // The project assembly is not loaded
- ERR_FAIL_V_MSG(false, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'.");
- } else {
- // The project assembly is loaded, but the class could not found
- ERR_FAIL_V_MSG(false, "Cannot instance script because the class '" + name + "' could not be found. Script: '" + get_path() + "'.");
- }
+ if (extra_cond && !valid) {
+ ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'.");
}
return valid && extra_cond;
}
StringName CSharpScript::get_instance_base_type() const {
- if (native) {
- return native->get_name();
- } else {
- return StringName();
- }
+ StringName native_name;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name);
+ return native_name;
}
CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
/* STEP 1, CREATE */
- // Search the constructor first, to fail with an error if it's not found before allocating anything else.
- GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
- if (ctor == nullptr) {
- ERR_FAIL_COND_V_MSG(p_argcount == 0, nullptr,
- "Cannot create script instance. The class '" + script_class->get_full_name() +
- "' does not define a parameterless constructor." +
- (get_path().is_empty() ? String() : " Path: '" + get_path() + "'."));
-
- ERR_FAIL_V_MSG(nullptr, "Constructor not found.");
- }
-
Ref<RefCounted> ref;
if (p_is_ref_counted) {
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
@@ -3163,15 +2276,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited && !script_binding.gchandle.is_released()) {
- MonoObject *mono_object = script_binding.gchandle.get_target();
- if (mono_object) {
- MonoException *exc = nullptr;
- GDMonoUtils::dispose(mono_object, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose(
+ script_binding.gchandle.get_intptr(), /* okIfNull */ true);
script_binding.gchandle.release(); // Just in case
script_binding.inited = false;
@@ -3185,38 +2291,19 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
/* STEP 2, INITIALIZE AND CONSTRUCT */
- MonoObject *mono_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
+ bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance(
+ this, p_owner, p_args, p_argcount);
- if (!mono_object) {
+ if (!ok) {
// Important to clear this before destroying the script instance here
instance->script = Ref<CSharpScript>();
instance->owner = nullptr;
-
- bool die = instance->_unreference_owner_unsafe();
- // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
- CRASH_COND(die);
-
p_owner->set_script_instance(nullptr);
- r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object.");
- }
-
- // Tie managed to unmanaged
- instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (instance->base_ref_counted) {
- instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
- }
-
- {
- MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
- instances.insert(instance->owner);
+ return nullptr;
}
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner);
-
- // Construct
- ctor->invoke(mono_object, p_args);
+ CRASH_COND(instance->gchandle.is_released());
/* STEP 3, PARTY */
@@ -3232,11 +2319,12 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
r_error.error = Callable::CallError::CALL_OK;
- ERR_FAIL_NULL_V(native, Variant());
+ StringName native_name;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name);
- GD_MONO_SCOPE_THREAD_ATTACH;
+ ERR_FAIL_COND_V(native_name == StringName(), Variant());
- Object *owner = ClassDB::instantiate(NATIVE_GDMONOCLASS_NAME(native));
+ Object *owner = ClassDB::instantiate(native_name);
Ref<RefCounted> ref;
RefCounted *r = Object::cast_to<RefCounted>(owner);
@@ -3264,18 +2352,18 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
CRASH_COND(!valid);
#endif
- GD_MONO_SCOPE_THREAD_ATTACH;
+ StringName native_name;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name);
- if (native) {
- StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
- if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
- if (EngineDebugger::is_active()) {
- CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
- "Script inherits from native type '" + String(native_name) +
- "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
- }
- ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'.");
+ ERR_FAIL_COND_V(native_name == StringName(), nullptr);
+
+ if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
+ if (EngineDebugger::is_active()) {
+ CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
+ "Script inherits from native type '" + String(native_name) +
+ "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
}
+ ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'.");
}
Callable::CallError unchecked_error;
@@ -3317,54 +2405,43 @@ void CSharpScript::set_source_code(const String &p_code) {
}
void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
- if (!script_class) {
+ if (!valid) {
return;
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- const Vector<GDMonoMethod *> &methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- MethodInfo minfo = methods[i]->get_method_info();
- if (minfo.name != CACHED_STRING_NAME(dotctor)) {
- p_list->push_back(methods[i]->get_method_info());
- }
+ const CSharpScript *top = this;
+ while (top != nullptr) {
+ for (const CSharpMethodInfo &E : top->methods) {
+ p_list->push_back(E.method_info);
}
- top = top->get_parent_class();
+ top = top->base_script.ptr();
}
}
bool CSharpScript::has_method(const StringName &p_method) const {
- if (!script_class) {
+ if (!valid) {
return false;
}
- GD_MONO_SCOPE_THREAD_ATTACH;
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ return true;
+ }
+ }
- return script_class->has_fetched_method_unknown_params(p_method);
+ return false;
}
MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
- if (!script_class) {
+ if (!valid) {
return MethodInfo();
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- GDMonoMethod *params = top->get_fetched_method_unknown_params(p_method);
- if (params) {
- return params->get_method_info();
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ return E.method_info;
}
-
- top = top->get_parent_class();
}
return MethodInfo();
@@ -3379,30 +2456,15 @@ Error CSharpScript::reload(bool p_keep_state) {
// That's done separately via domain reloading.
reload_invalidated = false;
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- const DotNetScriptLookupInfo *lookup_info =
- CSharpLanguage::get_singleton()->lookup_dotnet_script(get_path());
-
- if (lookup_info) {
- GDMonoClass *klass = lookup_info->script_class;
- if (klass) {
- ERR_FAIL_COND_V(!CACHED_CLASS(GodotObject)->is_assignable_from(klass), FAILED);
- script_class = klass;
- }
- }
+ String script_path = get_path();
- valid = script_class != nullptr;
+ valid = GDMonoCache::managed_callbacks.ScriptManagerBridge_AddScriptBridge(this, &script_path);
- if (script_class) {
+ if (valid) {
#ifdef DEBUG_ENABLED
- print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
+ print_verbose("Found class for script " + get_path());
#endif
- native = GDMonoUtils::get_class_native_base(script_class);
-
- CRASH_COND(native == nullptr);
-
update_script_class_info(this);
_update_exports();
@@ -3424,8 +2486,8 @@ bool CSharpScript::get_property_default_value(const StringName &p_property, Vari
return true;
}
- if (base_cache.is_valid()) {
- return base_cache->get_property_default_value(p_property, r_value);
+ if (base_script.is_valid()) {
+ return base_script->get_property_default_value(p_property, r_value);
}
#endif
@@ -3439,48 +2501,39 @@ void CSharpScript::update_exports() {
}
bool CSharpScript::has_script_signal(const StringName &p_signal) const {
- return event_signals.has(p_signal) || _signals.has(p_signal);
-}
-
-void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const KeyValue<StringName, Vector<SignalParameter>> &E : _signals) {
- MethodInfo mi;
- mi.name = E.key;
-
- const Vector<SignalParameter> &params = E.value;
- for (int i = 0; i < params.size(); i++) {
- const SignalParameter &param = params[i];
+ if (!valid) {
+ return false;
+ }
- PropertyInfo arg_info = PropertyInfo(param.type, param.name);
- if (param.type == Variant::NIL && param.nil_is_variant) {
- arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
+ if (!GDMonoCache::godot_api_cache_updated) {
+ return false;
+ }
- mi.arguments.push_back(arg_info);
+ for (const EventSignalInfo &signal : event_signals) {
+ if (signal.name == p_signal) {
+ return true;
}
-
- r_signals->push_back(mi);
}
- for (const KeyValue<StringName, EventSignal> &E : event_signals) {
- MethodInfo mi;
- mi.name = E.key;
-
- const EventSignal &event_signal = E.value;
- const Vector<SignalParameter> &params = event_signal.parameters;
- for (int i = 0; i < params.size(); i++) {
- const SignalParameter &param = params[i];
+ return false;
+}
- PropertyInfo arg_info = PropertyInfo(param.type, param.name);
- if (param.type == Variant::NIL && param.nil_is_variant) {
- arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
+void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ if (!valid) {
+ return;
+ }
- mi.arguments.push_back(arg_info);
- }
+ for (const EventSignalInfo &signal : get_script_event_signals()) {
+ r_signals->push_back(signal.method_info);
+ }
+}
- r_signals->push_back(mi);
+Vector<CSharpScript::EventSignalInfo> CSharpScript::get_script_event_signals() const {
+ if (!valid) {
+ return Vector<EventSignalInfo>();
}
+
+ return event_signals;
}
bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
@@ -3489,38 +2542,47 @@ bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
return false;
}
- if (script_class == nullptr || cs->script_class == nullptr) {
+ if (!valid || !cs->valid) {
return false;
}
- if (script_class == cs->script_class) {
- return true;
+ if (!GDMonoCache::godot_api_cache_updated) {
+ return false;
}
- return cs->script_class->is_assignable_from(script_class);
+ return GDMonoCache::managed_callbacks.ScriptManagerBridge_ScriptIsOrInherits(this, cs.ptr());
}
Ref<Script> CSharpScript::get_base_script() const {
- // TODO search in metadata file once we have it, not important any way?
- return Ref<Script>();
+ return base_script;
}
void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
- List<PropertyInfo> props;
-
#ifdef TOOLS_ENABLED
- for (const PropertyInfo &E : exported_members_cache) {
- props.push_back(E);
+ const CSharpScript *top = this;
+ while (top != nullptr) {
+ for (const PropertyInfo &E : top->exported_members_cache) {
+ r_list->push_back(E);
+ }
+
+ top = top->base_script.ptr();
}
#else
- for (const KeyValue<StringName, PropertyInfo> &E : member_info) {
- props.push_front(E.value);
- }
-#endif // TOOLS_ENABLED
+ const CSharpScript *top = this;
+ while (top != nullptr) {
+ List<PropertyInfo> props;
- for (const PropertyInfo &prop : props) {
- r_list->push_back(prop);
+ for (const KeyValue<StringName, PropertyInfo> &E : top->member_info) {
+ props.push_front(E.value);
+ }
+
+ for (const PropertyInfo &prop : props) {
+ r_list->push_back(prop);
+ }
+
+ top = top->base_script.ptr();
}
+#endif
}
int CSharpScript::get_member_line(const StringName &p_member) const {
@@ -3528,22 +2590,6 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
return -1;
}
-Variant CSharpScript::_member_get_rpc_config(IMonoClassMember *p_member) const {
- Variant out;
-
- MonoObject *rpc_attribute = p_member->get_attribute(CACHED_CLASS(RPCAttribute));
- if (rpc_attribute != nullptr) {
- Dictionary rpc_config;
- rpc_config["rpc_mode"] = CACHED_PROPERTY(RPCAttribute, Mode)->get_int_value(rpc_attribute);
- rpc_config["call_local"] = CACHED_PROPERTY(RPCAttribute, CallLocal)->get_bool_value(rpc_attribute);
- rpc_config["transfer_mode"] = CACHED_PROPERTY(RPCAttribute, TransferMode)->get_int_value(rpc_attribute);
- rpc_config["channel"] = CACHED_PROPERTY(RPCAttribute, TransferChannel)->get_int_value(rpc_attribute);
- out = rpc_config;
- }
-
- return out;
-}
-
const Variant CSharpScript::get_rpc_config() const {
return rpc_config;
}
@@ -3564,29 +2610,15 @@ Error CSharpScript::load_source_code(const String &p_path) {
return OK;
}
-void CSharpScript::_update_name() {
- String path = get_path();
-
- if (!path.is_empty()) {
- name = get_path().get_file().get_basename();
- }
-}
-
void CSharpScript::_clear() {
tool = false;
valid = false;
reload_invalidated = true;
-
- base = nullptr;
- native = nullptr;
- script_class = nullptr;
}
CSharpScript::CSharpScript() {
_clear();
- _update_name();
-
#ifdef DEBUG_ENABLED
{
MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
@@ -3600,6 +2632,10 @@ CSharpScript::~CSharpScript() {
MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
CSharpLanguage::get_singleton()->script_list.remove(&this->script_list);
#endif
+
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_RemoveScriptBridge(this);
+ }
}
void CSharpScript::get_members(HashSet<StringName> *p_members) {
@@ -3621,9 +2657,13 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
// TODO ignore anything inside bin/ and obj/ in tools builds?
- CSharpScript *script = memnew(CSharpScript);
+ Ref<CSharpScript> script;
- Ref<CSharpScript> scriptres(script);
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &script);
+ } else {
+ script = Ref<CSharpScript>(memnew(CSharpScript));
+ }
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
Error err = script->load_source_code(p_path);
@@ -3638,7 +2678,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
*r_error = OK;
}
- return scriptres;
+ return script;
}
void ResourceFormatLoaderCSharpScript::get_recognized_extensions(List<String> *p_extensions) const {
@@ -3701,14 +2741,7 @@ bool ResourceFormatSaverCSharpScript::recognize(const Ref<Resource> &p_resource)
}
CSharpLanguage::StringNameCache::StringNameCache() {
- _signal_callback = StaticCString::create("_signal_callback");
- _set = StaticCString::create("_set");
- _get = StaticCString::create("_get");
- _get_property_list = StaticCString::create("_get_property_list");
- _notification = StaticCString::create("_notification");
+ _property_can_revert = StaticCString::create("_property_can_revert");
+ _property_get_revert = StaticCString::create("_property_get_revert");
_script_source = StaticCString::create("script/source");
- on_before_serialize = StaticCString::create("OnBeforeSerialize");
- on_after_deserialize = StaticCString::create("OnAfterDeserialize");
- dotctor = StaticCString::create(".ctor");
- delegate_invoke_method_name = StaticCString::create("Invoke");
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 48129e69cb..3509a5c87d 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -39,8 +39,6 @@
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
-#include "mono_gd/gd_mono_header.h"
-#include "mono_gd/gd_mono_internals.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_plugin.h"
@@ -67,48 +65,17 @@ TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
#define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst))
-struct DotNetScriptLookupInfo {
- String class_namespace;
- String class_name;
- GDMonoClass *script_class = nullptr;
-
- DotNetScriptLookupInfo() {} // Required by HashMap...
-
- DotNetScriptLookupInfo(const String &p_class_namespace, const String &p_class_name, GDMonoClass *p_script_class) :
- class_namespace(p_class_namespace), class_name(p_class_name), script_class(p_script_class) {
- }
-};
-
class CSharpScript : public Script {
GDCLASS(CSharpScript, Script);
-public:
- struct SignalParameter {
- String name;
- Variant::Type type;
- bool nil_is_variant = false;
- };
-
- struct EventSignal {
- GDMonoField *field = nullptr;
- GDMonoMethod *invoke_method = nullptr;
- Vector<SignalParameter> parameters;
- };
-
-private:
friend class CSharpInstance;
friend class CSharpLanguage;
- friend struct CSharpScriptDepSort;
bool tool = false;
bool valid = false;
bool reload_invalidated = false;
- GDMonoClass *base = nullptr;
- GDMonoClass *native = nullptr;
- GDMonoClass *script_class = nullptr;
-
- Ref<CSharpScript> base_cache; // TODO what's this for?
+ Ref<CSharpScript> base_script;
HashSet<Object *> instances;
@@ -118,26 +85,32 @@ private:
// Replace with buffer containing the serialized state of managed scripts.
// Keep variant state backup to use only with script instance placeholders.
List<Pair<StringName, Variant>> properties;
- List<Pair<StringName, Array>> event_signals;
+ Dictionary event_signals;
};
HashSet<ObjectID> pending_reload_instances;
RBMap<ObjectID, StateBackup> pending_reload_state;
- StringName tied_class_name_for_reload;
- StringName tied_class_namespace_for_reload;
#endif
String source;
- StringName name;
SelfList<CSharpScript> script_list = this;
- HashMap<StringName, Vector<SignalParameter>> _signals;
- HashMap<StringName, EventSignal> event_signals;
- bool signals_invalidated = true;
-
Dictionary rpc_config;
+ struct EventSignalInfo {
+ StringName name; // MethodInfo stores a string...
+ MethodInfo method_info;
+ };
+
+ struct CSharpMethodInfo {
+ StringName name; // MethodInfo stores a string...
+ MethodInfo method_info;
+ };
+
+ Vector<EventSignalInfo> event_signals;
+ Vector<CSharpMethodInfo> methods;
+
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
HashMap<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
@@ -146,7 +119,6 @@ private:
bool placeholder_fallback_enabled = false;
bool exports_invalidated = true;
void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames);
- void _update_member_info_no_exports();
void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
@@ -158,39 +130,24 @@ private:
void _clear();
- void _update_name();
-
- void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
- bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
-
bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
- bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
-#ifdef TOOLS_ENABLED
- static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
-#endif
-
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
// Do not use unless you know what you are doing
- friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
- static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
static void update_script_class_info(Ref<CSharpScript> p_script);
- static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
-
- Variant _member_get_rpc_config(IMonoClassMember *p_member) const;
protected:
static void _bind_methods();
- Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
- void _resource_path_changed() override;
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
void _get_property_list(List<PropertyInfo> *p_properties) const;
public:
+ static void reload_registered_script(Ref<CSharpScript> p_script);
+
bool can_instantiate() const override;
StringName get_instance_base_type() const override;
ScriptInstance *instance_create(Object *p_this) override;
@@ -214,14 +171,20 @@ public:
bool has_script_signal(const StringName &p_signal) const override;
void get_script_signal_list(List<MethodInfo> *r_signals) const override;
+ Vector<EventSignalInfo> get_script_event_signals() const;
+
bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
void get_script_property_list(List<PropertyInfo> *r_list) const override;
void update_exports() override;
void get_members(HashSet<StringName> *p_members) override;
- bool is_tool() const override { return tool; }
- bool is_valid() const override { return valid; }
+ bool is_tool() const override {
+ return tool;
+ }
+ bool is_valid() const override {
+ return valid;
+ }
bool inherits_script(const Ref<Script> &p_script) const override;
@@ -237,7 +200,9 @@ public:
const Variant get_rpc_config() const override;
#ifdef TOOLS_ENABLED
- bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
+ bool is_placeholder_fallback_enabled() const override {
+ return placeholder_fallback_enabled;
+ }
#endif
Error load_source_code(const String &p_path);
@@ -270,22 +235,18 @@ class CSharpInstance : public ScriptInstance {
bool _unreference_owner_unsafe();
/*
- * If nullptr is returned, the caller must destroy the script instance by removing it from its owner.
+ * If false is returned, the caller must destroy the script instance by removing it from its owner.
*/
- MonoObject *_internal_new_managed();
+ bool _internal_new_managed();
// Do not use unless you know what you are doing
- friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle);
- void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
- void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
-
public:
- MonoObject *get_mono_object() const;
-
_FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; }
+ _FORCE_INLINE_ GCHandleIntPtr get_gchandle_intptr() { return gchandle.get_intptr(); }
+
Object *get_owner() override;
bool set(const StringName &p_name, const Variant &p_value) override;
@@ -293,17 +254,20 @@ public:
void get_property_list(List<PropertyInfo> *p_properties) const override;
Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override;
+ bool property_can_revert(const StringName &p_name) const override;
+ bool property_get_revert(const StringName &p_name, Variant &r_ret) const override;
+
void get_method_list(List<MethodInfo> *p_list) const override;
bool has_method(const StringName &p_method) const override;
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
- void mono_object_disposed(MonoObject *p_obj);
+ void mono_object_disposed(GCHandleIntPtr p_gchandle_to_free);
/*
* If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
- void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
+ void mono_object_disposed_baseref(GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
void connect_event_signals();
void disconnect_event_signals();
@@ -329,7 +293,6 @@ public:
struct CSharpScriptBinding {
bool inited = false;
StringName type_name;
- GDMonoClass *wrapper_class = nullptr;
MonoGCHandleData gchandle;
Object *owner = nullptr;
@@ -367,33 +330,22 @@ class CSharpLanguage : public ScriptLanguage {
ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman);
struct StringNameCache {
- StringName _signal_callback;
- StringName _set;
- StringName _get;
- StringName _get_property_list;
- StringName _notification;
+ StringName _property_can_revert;
+ StringName _property_get_revert;
StringName _script_source;
- StringName dotctor; // .ctor
- StringName on_before_serialize; // OnBeforeSerialize
- StringName on_after_deserialize; // OnAfterDeserialize
- StringName delegate_invoke_method_name;
StringNameCache();
};
int lang_idx = -1;
- HashMap<String, DotNetScriptLookupInfo> dotnet_script_lookup_map;
-
- void lookup_script_for_class(GDMonoClass *p_class);
-
// For debug_break and debug_break_parse
int _debug_parse_err_line = -1;
String _debug_parse_err_file;
String _debug_error;
friend class GDMono;
- void _on_scripts_domain_unloaded();
+ void _on_scripts_domain_about_to_unload();
#ifdef TOOLS_ENABLED
EditorPlugin *godotsharp_editor = nullptr;
@@ -415,21 +367,35 @@ public:
StringNameCache string_names;
- const Mutex &get_language_bind_mutex() { return language_bind_mutex; }
+ const Mutex &get_language_bind_mutex() {
+ return language_bind_mutex;
+ }
+ const Mutex &get_script_instances_mutex() {
+ return script_instances_mutex;
+ }
- _FORCE_INLINE_ int get_language_index() { return lang_idx; }
+ _FORCE_INLINE_ int get_language_index() {
+ return lang_idx;
+ }
void set_language_index(int p_idx);
- _FORCE_INLINE_ const StringNameCache &get_string_names() { return string_names; }
+ _FORCE_INLINE_ const StringNameCache &get_string_names() {
+ return string_names;
+ }
- _FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
+ _FORCE_INLINE_ static CSharpLanguage *get_singleton() {
+ return singleton;
+ }
#ifdef TOOLS_ENABLED
- _FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const { return godotsharp_editor; }
+ _FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const {
+ return godotsharp_editor;
+ }
#endif
static void release_script_gchandle(MonoGCHandleData &p_gchandle);
- static void release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle);
+ static void release_script_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, MonoGCHandleData &r_gchandle);
+ static void release_binding_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, CSharpScriptBinding &r_script_binding);
bool debug_break(const String &p_error, bool p_allow_continue = true);
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
@@ -439,12 +405,8 @@ public:
void reload_assemblies(bool p_soft_reload);
#endif
- _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
-
- void lookup_scripts_in_assembly(GDMonoAssembly *p_assembly);
-
- const DotNetScriptLookupInfo *lookup_dotnet_script(const String &p_script_path) const {
- return dotnet_script_lookup_map.getptr(p_script_path);
+ _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const {
+ return managed_callable_middleman;
}
String get_name() const override;
@@ -474,7 +436,9 @@ public:
Script *create_script() const override;
bool has_named_classes() const override;
bool supports_builtin_mode() const override;
- /* TODO? */ int find_function(const String &p_function, const String &p_code) const override { return -1; }
+ /* TODO? */ int find_function(const String &p_function, const String &p_code) const override {
+ return -1;
+ }
String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override;
virtual String _get_indentation() const;
/* TODO? */ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {}
@@ -489,14 +453,20 @@ public:
/* TODO */ void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) override {}
/* TODO */ void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) override {}
/* TODO */ void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) override {}
- /* TODO */ String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) override { return ""; }
+ /* TODO */ String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) override {
+ return "";
+ }
Vector<StackInfo> debug_get_current_stack_info() override;
/* PROFILING FUNCTIONS */
/* TODO */ void profiling_start() override {}
/* TODO */ void profiling_stop() override {}
- /* TODO */ int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override { return 0; }
- /* TODO */ int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override { return 0; }
+ /* TODO */ int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override {
+ return 0;
+ }
+ /* TODO */ int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override {
+ return 0;
+ }
void frame() override;
@@ -515,16 +485,12 @@ public:
bool overrides_external_editor() override;
#endif
- /* THREAD ATTACHING */
- void thread_enter() override;
- void thread_exit() override;
-
RBMap<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
-#ifdef DEBUG_ENABLED
- Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
-#endif
+ static void tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted);
+ static void tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, Ref<CSharpScript> *p_script, bool p_ref_counted);
+ static void tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged);
void post_unsafe_reference(Object *p_obj);
void pre_unsafe_unreference(Object *p_obj);
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index b981542801..faf3512da7 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -10,55 +10,10 @@
<tutorials>
</tutorials>
<methods>
- <method name="attach_thread">
- <return type="void" />
- <description>
- Attaches the current thread to the Mono runtime.
- </description>
- </method>
- <method name="detach_thread">
- <return type="void" />
- <description>
- Detaches the current thread from the Mono runtime.
- </description>
- </method>
- <method name="get_domain_id">
- <return type="int" />
- <description>
- Returns the current MonoDomain ID.
- [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
- </description>
- </method>
- <method name="get_scripts_domain_id">
- <return type="int" />
- <description>
- Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded.
- [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
- </description>
- </method>
- <method name="is_domain_finalizing_for_unload">
- <return type="bool" />
- <param index="0" name="domain_id" type="int" />
- <description>
- Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
- </description>
- </method>
<method name="is_runtime_initialized">
<return type="bool" />
<description>
- Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise.
- </description>
- </method>
- <method name="is_runtime_shutting_down">
- <return type="bool" />
- <description>
- Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise.
- </description>
- </method>
- <method name="is_scripts_domain_loaded">
- <return type="bool" />
- <description>
- Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise.
+ Returns [code]true[/code] if the .NET runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
</methods>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
index d1868f52ef..03a7dc453c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}"
EndProject
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
index 4e9e7184da..013b210ff4 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -26,16 +26,8 @@
<None Include="Sdk\Sdk.props" Pack="true" PackagePath="Sdk" />
<None Include="Sdk\Sdk.targets" Pack="true" PackagePath="Sdk" />
<!-- SdkPackageVersions.props -->
- <None Include="..\..\..\SdkPackageVersions.props" Pack="true" PackagePath="Sdk">
+ <None Include="$(GodotSdkPackageVersionsFilePath)" Pack="true" PackagePath="Sdk">
<Link>Sdk\SdkPackageVersions.props</Link>
</None>
</ItemGroup>
-
- <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
- <PropertyGroup>
- <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
- <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
- </PropertyGroup>
- <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
- </Target>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index 5a499742e9..ad41ab04d5 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -77,7 +77,6 @@
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'windows' ">GODOT_WINDOWS;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'linuxbsd' ">GODOT_LINUXBSD;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'macos' ">GODOT_OSX;GODOT_MACOS;GODOT_PC</GodotPlatformConstants>
- <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'server' ">GODOT_SERVER;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'uwp' ">GODOT_UWP;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'haiku' ">GODOT_HAIKU;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'android' ">GODOT_ANDROID;GODOT_MOBILE</GodotPlatformConstants>
@@ -95,21 +94,4 @@
<DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
</PropertyGroup>
-
- <!-- Godot API references -->
- <ItemGroup>
- <!--
- TODO:
- We should consider a nuget package for reference assemblies. This is difficult because the
- Godot scripting API is continuaslly breaking backwards compatibility even in patch releases.
- -->
- <Reference Include="GodotSharp">
- <Private>false</Private>
- <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath>
- </Reference>
- <Reference Include="GodotSharpEditor" Condition=" '$(Configuration)' == 'Debug' ">
- <Private>false</Private>
- <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath>
- </Reference>
- </ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
index 397ede9644..aad4ea4553 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
@@ -19,4 +19,10 @@
<ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
<PackageReference Include="Godot.SourceGenerators" Version="$(PackageFloatingVersion_Godot)" />
</ItemGroup>
+
+ <!-- Godot API references -->
+ <ItemGroup Condition=" '$(DisableImplicitGodotSharpReferences)' != 'true' ">
+ <PackageReference Include="GodotSharp" Version="$(PackageVersion_GodotSharp)" />
+ <PackageReference Include="GodotSharpEditor" Version="$(PackageVersion_GodotSharp)" Condition=" '$(Configuration)' == 'Debug' " />
+ </ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs
new file mode 100644
index 0000000000..764ba8f121
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs
@@ -0,0 +1,7 @@
+namespace Godot.SourceGenerators.Sample;
+
+public partial class EventSignals : Godot.Object
+{
+ [Signal]
+ public delegate void MySignalEventHandler(string str, int num);
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs
new file mode 100644
index 0000000000..ac9f59aa99
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+#pragma warning disable CS0169
+#pragma warning disable CS0414
+
+namespace Godot.SourceGenerators.Sample
+{
+ [SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")]
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public partial class ExportedFields : Godot.Object
+ {
+ [Export] private Boolean field_Boolean = true;
+ [Export] private Char field_Char = 'f';
+ [Export] private SByte field_SByte = 10;
+ [Export] private Int16 field_Int16 = 10;
+ [Export] private Int32 field_Int32 = 10;
+ [Export] private Int64 field_Int64 = 10;
+ [Export] private Byte field_Byte = 10;
+ [Export] private UInt16 field_UInt16 = 10;
+ [Export] private UInt32 field_UInt32 = 10;
+ [Export] private UInt64 field_UInt64 = 10;
+ [Export] private Single field_Single = 10;
+ [Export] private Double field_Double = 10;
+ [Export] private String field_String = "foo";
+
+ // Godot structs
+ [Export] private Vector2 field_Vector2 = new(10f, 10f);
+ [Export] private Vector2i field_Vector2i = Vector2i.Up;
+ [Export] private Rect2 field_Rect2 = new(new Vector2(10f, 10f), new Vector2(10f, 10f));
+ [Export] private Rect2i field_Rect2i = new(new Vector2i(10, 10), new Vector2i(10, 10));
+ [Export] private Transform2D field_Transform2D = Transform2D.Identity;
+ [Export] private Vector3 field_Vector3 = new(10f, 10f, 10f);
+ [Export] private Vector3i field_Vector3i = Vector3i.Back;
+ [Export] private Basis field_Basis = new Basis(Quaternion.Identity);
+ [Export] private Quaternion field_Quaternion = new Quaternion(Basis.Identity);
+ [Export] private Transform3D field_Transform3D = Transform3D.Identity;
+ [Export] private Vector4 field_Vector4 = new(10f, 10f, 10f, 10f);
+ [Export] private Vector4i field_Vector4i = Vector4i.One;
+ [Export] private Projection field_Projection = Projection.Identity;
+ [Export] private AABB field_AABB = new AABB(10f, 10f, 10f, new Vector3(1f, 1f, 1f));
+ [Export] private Color field_Color = Colors.Aquamarine;
+ [Export] private Plane field_Plane = Plane.PlaneXZ;
+ [Export] private Callable field_Callable = new Callable(Engine.GetMainLoop(), "_process");
+ [Export] private SignalInfo field_SignalInfo = new SignalInfo(Engine.GetMainLoop(), "property_list_changed");
+
+ // Enums
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyEnum field_Enum = MyEnum.C;
+
+ [Flags]
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyFlagsEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyFlagsEnum field_FlagsEnum = MyFlagsEnum.C;
+
+ // Arrays
+ [Export] private Byte[] field_ByteArray = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int32[] field_Int32Array = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int64[] field_Int64Array = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Single[] field_SingleArray = { 0f, 1f, 2f, 3f, 4f, 5f, 6f };
+ [Export] private Double[] field_DoubleArray = { 0d, 1d, 2d, 3d, 4d, 5d, 6d };
+ [Export] private String[] field_StringArray = { "foo", "bar" };
+ [Export(PropertyHint.Enum, "A,B,C")] private String[] field_StringArrayEnum = { "foo", "bar" };
+ [Export] private Vector2[] field_Vector2Array = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right };
+ [Export] private Vector3[] field_Vector3Array = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
+ [Export] private Color[] field_ColorArray = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
+ [Export] private Godot.Object[] field_GodotObjectOrDerivedArray = { null };
+ [Export] private StringName[] field_StringNameArray = { "foo", "bar" };
+ [Export] private NodePath[] field_NodePathArray = { "foo", "bar" };
+ [Export] private RID[] field_RIDArray = { default, default, default };
+
+ // Variant
+ [Export] private Variant field_Variant = "foo";
+
+ // Classes
+ [Export] private Godot.Object field_GodotObjectOrDerived;
+ [Export] private Godot.Texture field_GodotResourceTexture;
+ [Export] private StringName field_StringName = new StringName("foo");
+ [Export] private NodePath field_NodePath = new NodePath("foo");
+ [Export] private RID field_RID;
+
+ [Export] private Godot.Collections.Dictionary field_GodotDictionary =
+ new() { { "foo", 10 }, { Vector2.Up, Colors.Chocolate } };
+
+ [Export] private Godot.Collections.Array field_GodotArray =
+ new() { "foo", 10, Vector2.Up, Colors.Chocolate };
+
+ [Export] private Godot.Collections.Dictionary<string, bool> field_GodotGenericDictionary =
+ new() { { "foo", true }, { "bar", false } };
+
+ [Export] private Godot.Collections.Array<int> field_GodotGenericArray =
+ new() { 0, 1, 2, 3, 4, 5, 6 };
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs
new file mode 100644
index 0000000000..4a0e8075f0
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+#pragma warning disable CS0169
+#pragma warning disable CS0414
+
+namespace Godot.SourceGenerators.Sample
+{
+ [SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")]
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public partial class ExportedProperties : Godot.Object
+ {
+ [Export] private Boolean property_Boolean { get; set; } = true;
+ [Export] private Char property_Char { get; set; } = 'f';
+ [Export] private SByte property_SByte { get; set; } = 10;
+ [Export] private Int16 property_Int16 { get; set; } = 10;
+ [Export] private Int32 property_Int32 { get; set; } = 10;
+ [Export] private Int64 property_Int64 { get; set; } = 10;
+ [Export] private Byte property_Byte { get; set; } = 10;
+ [Export] private UInt16 property_UInt16 { get; set; } = 10;
+ [Export] private UInt32 property_UInt32 { get; set; } = 10;
+ [Export] private UInt64 property_UInt64 { get; set; } = 10;
+ [Export] private Single property_Single { get; set; } = 10;
+ [Export] private Double property_Double { get; set; } = 10;
+ [Export] private String property_String { get; set; } = "foo";
+
+ // Godot structs
+ [Export] private Vector2 property_Vector2 { get; set; } = new(10f, 10f);
+ [Export] private Vector2i property_Vector2i { get; set; } = Vector2i.Up;
+ [Export] private Rect2 property_Rect2 { get; set; } = new(new Vector2(10f, 10f), new Vector2(10f, 10f));
+ [Export] private Rect2i property_Rect2i { get; set; } = new(new Vector2i(10, 10), new Vector2i(10, 10));
+ [Export] private Transform2D property_Transform2D { get; set; } = Transform2D.Identity;
+ [Export] private Vector3 property_Vector3 { get; set; } = new(10f, 10f, 10f);
+ [Export] private Vector3i property_Vector3i { get; set; } = Vector3i.Back;
+ [Export] private Basis property_Basis { get; set; } = new Basis(Quaternion.Identity);
+ [Export] private Quaternion property_Quaternion { get; set; } = new Quaternion(Basis.Identity);
+ [Export] private Transform3D property_Transform3D { get; set; } = Transform3D.Identity;
+ [Export] private Vector4 property_Vector4 { get; set; } = new(10f, 10f, 10f, 10f);
+ [Export] private Vector4i property_Vector4i { get; set; } = Vector4i.One;
+ [Export] private Projection property_Projection { get; set; } = Projection.Identity;
+ [Export] private AABB property_AABB { get; set; } = new AABB(10f, 10f, 10f, new Vector3(1f, 1f, 1f));
+ [Export] private Color property_Color { get; set; } = Colors.Aquamarine;
+ [Export] private Plane property_Plane { get; set; } = Plane.PlaneXZ;
+ [Export] private Callable property_Callable { get; set; } = new Callable(Engine.GetMainLoop(), "_process");
+ [Export] private SignalInfo property_SignalInfo { get; set; } = new SignalInfo(Engine.GetMainLoop(), "property_list_changed");
+
+ // Enums
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyEnum property_Enum { get; set; } = MyEnum.C;
+
+ [Flags]
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyFlagsEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyFlagsEnum property_FlagsEnum { get; set; } = MyFlagsEnum.C;
+
+ // Arrays
+ [Export] private Byte[] property_ByteArray { get; set; } = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int32[] property_Int32Array { get; set; } = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int64[] property_Int64Array { get; set; } = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Single[] property_SingleArray { get; set; } = { 0f, 1f, 2f, 3f, 4f, 5f, 6f };
+ [Export] private Double[] property_DoubleArray { get; set; } = { 0d, 1d, 2d, 3d, 4d, 5d, 6d };
+ [Export] private String[] property_StringArray { get; set; } = { "foo", "bar" };
+ [Export(PropertyHint.Enum, "A,B,C")] private String[] property_StringArrayEnum { get; set; } = { "foo", "bar" };
+ [Export] private Vector2[] property_Vector2Array { get; set; } = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right };
+ [Export] private Vector3[] property_Vector3Array { get; set; } = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
+ [Export] private Color[] property_ColorArray { get; set; } = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
+ [Export] private Godot.Object[] property_GodotObjectOrDerivedArray { get; set; } = { null };
+ [Export] private StringName[] field_StringNameArray { get; set; } = { "foo", "bar" };
+ [Export] private NodePath[] field_NodePathArray { get; set; } = { "foo", "bar" };
+ [Export] private RID[] field_RIDArray { get; set; } = { default, default, default };
+
+ // Variant
+ [Export] private Variant property_Variant { get; set; } = "foo";
+
+ // Classes
+ [Export] private Godot.Object property_GodotObjectOrDerived { get; set; }
+ [Export] private Godot.Texture property_GodotResourceTexture { get; set; }
+ [Export] private StringName property_StringName { get; set; } = new StringName("foo");
+ [Export] private NodePath property_NodePath { get; set; } = new NodePath("foo");
+ [Export] private RID property_RID { get; set; }
+
+ [Export] private Godot.Collections.Dictionary property_GodotDictionary { get; set; } =
+ new() { { "foo", 10 }, { Vector2.Up, Colors.Chocolate } };
+
+ [Export] private Godot.Collections.Array property_GodotArray { get; set; } =
+ new() { "foo", 10, Vector2.Up, Colors.Chocolate };
+
+ [Export] private Godot.Collections.Dictionary<string, bool> property_GodotGenericDictionary { get; set; } =
+ new() { { "foo", true }, { "bar", false } };
+
+ [Export] private Godot.Collections.Array<int> property_GodotGenericArray { get; set; } =
+ new() { 0, 1, 2, 3, 4, 5, 6 };
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
index 2ddb8880c2..b21b035b4d 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
@@ -1,16 +1,21 @@
+#pragma warning disable CS0169
+
namespace Godot.SourceGenerators.Sample
{
partial class Generic<T> : Godot.Object
{
+ private int _field;
}
// Generic again but different generic parameters
partial class Generic<T, R> : Godot.Object
{
+ private int _field;
}
// Generic again but without generic parameters
partial class Generic : Godot.Object
{
+ private int _field;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
index 24f7909861..8e78e0385d 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
@@ -1,12 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<!-- $(GodotProjectDir) would normally be defined by the Godot.NET.Sdk -->
<GodotProjectDir>$(MSBuildProjectDirectory)</GodotProjectDir>
+ <!-- For compiling GetGodotPropertyDefaultValues. -->
+ <DefineConstants>$(DefineConstants);TOOLS</DefineConstants>
</PropertyGroup>
<PropertyGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs
new file mode 100644
index 0000000000..618ba24abc
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs
@@ -0,0 +1,31 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.SourceGenerators.Sample;
+
+[SuppressMessage("ReSharper", "RedundantNameQualifier")]
+public partial class Methods : Godot.Object
+{
+ private void MethodWithOverload()
+ {
+ }
+
+ private void MethodWithOverload(int a)
+ {
+ }
+
+ private void MethodWithOverload(int a, int b)
+ {
+ }
+
+ // Should be ignored. The previous one is picked.
+ // ReSharper disable once UnusedMember.Local
+ private void MethodWithOverload(float a, float b)
+ {
+ }
+
+ // Generic methods should be ignored.
+ // ReSharper disable once UnusedMember.Local
+ private void GenericMethod<T>(T t)
+ {
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs
new file mode 100644
index 0000000000..a1667dbb8f
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs
@@ -0,0 +1,36 @@
+#pragma warning disable CS0169
+
+namespace Godot.SourceGenerators.Sample
+{
+ public partial class ScriptBoilerplate : Node
+ {
+ private NodePath _nodePath;
+ private int _velocity;
+
+ public override void _Process(float delta)
+ {
+ _ = delta;
+
+ base._Process(delta);
+ }
+
+ public int Bazz(StringName name)
+ {
+ _ = name;
+ return 1;
+ }
+
+ public void IgnoreThisMethodWithByRefParams(ref int a)
+ {
+ _ = a;
+ }
+ }
+
+ partial struct OuterClass
+ {
+ public partial class NesterClass : RefCounted
+ {
+ public override Variant _Get(StringName property) => default;
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 4867c986e6..3dfa8000ba 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -1,5 +1,7 @@
+using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace Godot.SourceGenerators
{
@@ -14,12 +16,11 @@ namespace Godot.SourceGenerators
"Missing partial modifier on declaration of type '" +
$"{symbol.FullQualifiedName()}' which is a subclass of '{GodotClasses.Object}'";
- string description = $"{message}. Subclasses of '{GodotClasses.Object}' must be " +
- "declared with the partial modifier or annotated with the " +
- $"attribute '{GodotClasses.DisableGodotGeneratorsAttr}'.";
+ string description = $"{message}. Subclasses of '{GodotClasses.Object}' " +
+ "must be declared with the partial modifier.";
context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GODOT-G0001",
+ new DiagnosticDescriptor(id: "GD0001",
title: message,
messageFormat: message,
category: "Usage",
@@ -29,5 +30,307 @@ namespace Godot.SourceGenerators
cds.GetLocation(),
cds.SyntaxTree.FilePath));
}
+
+ public static void ReportNonPartialGodotScriptOuterClass(
+ GeneratorExecutionContext context,
+ TypeDeclarationSyntax outerTypeDeclSyntax
+ )
+ {
+ var outerSymbol = context.Compilation
+ .GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
+ .GetDeclaredSymbol(outerTypeDeclSyntax);
+
+ string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
+ namedTypeSymbol.FullQualifiedName() :
+ "type not found";
+
+ string message =
+ $"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
+ $"which contains one or more subclasses of '{GodotClasses.Object}'";
+
+ string description = $"{message}. Subclasses of '{GodotClasses.Object}' and their " +
+ "containing types must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0002",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ outerTypeDeclSyntax.GetLocation(),
+ outerTypeDeclSyntax.SyntaxTree.FilePath));
+ }
+
+ public static void ReportExportedMemberIsStatic(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ bool isField = exportedMemberSymbol is IFieldSymbol;
+
+ string message = $"Attempted to export static {(isField ? "field" : "property")}: " +
+ $"'{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Only instance fields and properties can be exported." +
+ " Remove the 'static' modifier or the '[Export]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0101",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportExportedMemberTypeNotSupported(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ bool isField = exportedMemberSymbol is IFieldSymbol;
+
+ string message = $"The type of the exported {(isField ? "field" : "property")} " +
+ $"is not supported: '{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Use a supported type or remove the '[Export]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0102",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportExportedMemberIsReadOnly(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ bool isField = exportedMemberSymbol is IFieldSymbol;
+
+ string message = $"The exported {(isField ? "field" : "property")} " +
+ $"is read-only: '{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = isField ?
+ $"{message}. Exported fields cannot be read-only." :
+ $"{message}. Exported properties must be writable.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0103",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportExportedMemberIsWriteOnly(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = $"The exported property is write-only: '{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Exported properties must be readable.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0104",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportSignalDelegateMissingSuffix(
+ GeneratorExecutionContext context,
+ INamedTypeSymbol delegateSymbol)
+ {
+ var locations = delegateSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = "The name of the delegate must end with 'EventHandler': " +
+ delegateSymbol.ToDisplayString() +
+ $". Did you mean '{delegateSymbol.Name}EventHandler'?";
+
+ string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0201",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportSignalParameterTypeNotSupported(
+ GeneratorExecutionContext context,
+ IParameterSymbol parameterSymbol)
+ {
+ var locations = parameterSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = "The parameter of the delegate signature of the signal " +
+ $"is not supported: '{parameterSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0202",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportSignalDelegateSignatureMustReturnVoid(
+ GeneratorExecutionContext context,
+ INamedTypeSymbol delegateSymbol)
+ {
+ var locations = delegateSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = "The delegate signature of the signal " +
+ $"must return void: '{delegateSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Return void or remove the '[Signal]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0203",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule =
+ new DiagnosticDescriptor(id: "GD0301",
+ title: "The generic type argument must be a Variant compatible type",
+ messageFormat: "The generic type argument must be a Variant compatible type: {0}",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.");
+
+ public static void ReportGenericTypeArgumentMustBeVariant(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode typeArgumentSyntax,
+ ISymbol typeArgumentSymbol)
+ {
+ string message = "The generic type argument " +
+ $"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Use a Variant compatible type as the generic type argument.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0301",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ typeArgumentSyntax.GetLocation(),
+ typeArgumentSyntax.SyntaxTree.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule =
+ new DiagnosticDescriptor(id: "GD0302",
+ title: "The generic type parameter must be annotated with the MustBeVariant attribute",
+ messageFormat: "The generic type argument must be a Variant type: {0}",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
+
+ public static void ReportGenericTypeParameterMustBeVariantAnnotated(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode typeArgumentSyntax,
+ ISymbol typeArgumentSymbol)
+ {
+ string message = "The generic type parameter must be annotated with the MustBeVariant attribute";
+
+ string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0302",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ typeArgumentSyntax.GetLocation(),
+ typeArgumentSyntax.SyntaxTree.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule =
+ new DiagnosticDescriptor(id: "GD0303",
+ title: "The generic type parameter must be annotated with the MustBeVariant attribute",
+ messageFormat: "The generic type argument must be a Variant type: {0}",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
+
+ public static void ReportTypeArgumentParentSymbolUnhandled(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode typeArgumentSyntax,
+ ISymbol parentSymbol)
+ {
+ string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " +
+ "that must be Variant compatible was not handled.";
+
+ string description = $"{message}. Handle type arguments that are children of the unhandled symbol type.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0303",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ typeArgumentSyntax.GetLocation(),
+ typeArgumentSyntax.SyntaxTree.FilePath));
+ }
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index e16f72f43a..d868678179 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -1,4 +1,6 @@
+using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
@@ -13,30 +15,65 @@ namespace Godot.SourceGenerators
) => context.AnalyzerConfigOptions.GlobalOptions
.TryGetValue("build_property." + property, out value);
- private static bool InheritsFrom(this INamedTypeSymbol? symbol, string baseName)
- {
- if (symbol == null)
- return false;
+ public static bool AreGodotSourceGeneratorsDisabled(this GeneratorExecutionContext context)
+ => context.TryGetGlobalAnalyzerProperty("GodotSourceGenerators", out string? toggle) &&
+ toggle != null &&
+ toggle.Equals("disabled", StringComparison.OrdinalIgnoreCase);
+
+ public static bool IsGodotToolsProject(this GeneratorExecutionContext context)
+ => context.TryGetGlobalAnalyzerProperty("IsGodotToolsProject", out string? toggle) &&
+ toggle != null &&
+ toggle.Equals("true", StringComparison.OrdinalIgnoreCase);
- while (true)
+ public static bool InheritsFrom(this INamedTypeSymbol? symbol, string assemblyName, string typeFullName)
+ {
+ while (symbol != null)
{
- if (symbol.ToString() == baseName)
+ if (symbol.ContainingAssembly.Name == assemblyName &&
+ symbol.ToString() == typeFullName)
{
return true;
}
- if (symbol.BaseType != null)
- {
- symbol = symbol.BaseType;
- continue;
- }
-
- break;
+ symbol = symbol.BaseType;
}
return false;
}
+ public static INamedTypeSymbol? GetGodotScriptNativeClass(this INamedTypeSymbol classTypeSymbol)
+ {
+ var symbol = classTypeSymbol;
+
+ while (symbol != null)
+ {
+ if (symbol.ContainingAssembly.Name == "GodotSharp")
+ return symbol;
+
+ symbol = symbol.BaseType;
+ }
+
+ return null;
+ }
+
+ public static string? GetGodotScriptNativeClassName(this INamedTypeSymbol classTypeSymbol)
+ {
+ var nativeType = classTypeSymbol.GetGodotScriptNativeClass();
+
+ if (nativeType == null)
+ return null;
+
+ var godotClassNameAttr = nativeType.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.IsGodotClassNameAttribute() ?? false);
+
+ string? godotClassName = null;
+
+ if (godotClassNameAttr is { ConstructorArguments: { Length: > 0 } })
+ godotClassName = godotClassNameAttr.ConstructorArguments[0].Value?.ToString();
+
+ return godotClassName ?? nativeType.Name;
+ }
+
private static bool IsGodotScriptClass(
this ClassDeclarationSyntax cds, Compilation compilation,
out INamedTypeSymbol? symbol
@@ -47,7 +84,7 @@ namespace Godot.SourceGenerators
var classTypeSymbol = sm.GetDeclaredSymbol(cds);
if (classTypeSymbol?.BaseType == null
- || !classTypeSymbol.BaseType.InheritsFrom(GodotClasses.Object))
+ || !classTypeSymbol.BaseType.InheritsFrom("GodotSharp", GodotClasses.Object))
{
symbol = null;
return false;
@@ -69,21 +106,174 @@ namespace Godot.SourceGenerators
}
}
- public static bool IsPartial(this ClassDeclarationSyntax cds)
+ public static bool IsNested(this TypeDeclarationSyntax cds)
+ => cds.Parent is TypeDeclarationSyntax;
+
+ public static bool IsPartial(this TypeDeclarationSyntax cds)
=> cds.Modifiers.Any(SyntaxKind.PartialKeyword);
- public static bool HasDisableGeneratorsAttribute(this INamedTypeSymbol symbol)
- => symbol.GetAttributes().Any(attr =>
- attr.AttributeClass?.ToString() == GodotClasses.DisableGodotGeneratorsAttr);
+ public static bool AreAllOuterTypesPartial(
+ this TypeDeclarationSyntax cds,
+ out TypeDeclarationSyntax? typeMissingPartial
+ )
+ {
+ SyntaxNode? outerSyntaxNode = cds.Parent;
+
+ while (outerSyntaxNode is TypeDeclarationSyntax outerTypeDeclSyntax)
+ {
+ if (!outerTypeDeclSyntax.IsPartial())
+ {
+ typeMissingPartial = outerTypeDeclSyntax;
+ return false;
+ }
+
+ outerSyntaxNode = outerSyntaxNode.Parent;
+ }
+
+ typeMissingPartial = null;
+ return true;
+ }
+
+ public static string GetDeclarationKeyword(this INamedTypeSymbol namedTypeSymbol)
+ {
+ string? keyword = namedTypeSymbol.DeclaringSyntaxReferences
+ .OfType<TypeDeclarationSyntax>().FirstOrDefault()?
+ .Keyword.Text;
+
+ return keyword ?? namedTypeSymbol.TypeKind switch
+ {
+ TypeKind.Interface => "interface",
+ TypeKind.Struct => "struct",
+ _ => "class"
+ };
+ }
private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
SymbolDisplayFormat.FullyQualifiedFormat
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
- public static string FullQualifiedName(this INamedTypeSymbol symbol)
+ public static string FullQualifiedName(this ITypeSymbol symbol)
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
+ public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
+ {
+ return symbol.IsGenericType ?
+ string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") :
+ symbol.Name;
+ }
+
public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
+
+ public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
+ => qualifiedName
+ // AddSource() doesn't support angle brackets
+ .Replace("<", "(Of ")
+ .Replace(">", ")");
+
+ public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.ExportAttr;
+
+ public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.SignalAttr;
+
+ public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.MustBeVariantAttr;
+
+ public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.GodotClassNameAttr;
+
+ public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.SystemFlagsAttr;
+
+ public static GodotMethodData? HasGodotCompatibleSignature(
+ this IMethodSymbol method,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ if (method.IsGenericMethod)
+ return null;
+
+ var retSymbol = method.ReturnType;
+ var retType = method.ReturnsVoid ?
+ null :
+ MarshalUtils.ConvertManagedTypeToMarshalType(method.ReturnType, typeCache);
+
+ if (retType == null && !method.ReturnsVoid)
+ return null;
+
+ var parameters = method.Parameters;
+
+ var paramTypes = parameters
+ // Currently we don't support `ref`, `out`, `in`, `ref readonly` parameters (and we never may)
+ .Where(p => p.RefKind == RefKind.None)
+ // Attempt to determine the variant type
+ .Select(p => MarshalUtils.ConvertManagedTypeToMarshalType(p.Type, typeCache))
+ // Discard parameter types that couldn't be determined (null entries)
+ .Where(t => t != null).Cast<MarshalType>().ToImmutableArray();
+
+ // If any parameter type was incompatible, it was discarded so the length won't match
+ if (parameters.Length > paramTypes.Length)
+ return null; // Ignore incompatible method
+
+ return new GodotMethodData(method, paramTypes, parameters
+ .Select(p => p.Type).ToImmutableArray(), retType, retSymbol);
+ }
+
+ public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature(
+ this IEnumerable<IMethodSymbol> methods,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var method in methods)
+ {
+ var methodData = HasGodotCompatibleSignature(method, typeCache);
+
+ if (methodData != null)
+ yield return methodData.Value;
+ }
+ }
+
+ public static IEnumerable<GodotPropertyData> WhereIsGodotCompatibleType(
+ this IEnumerable<IPropertySymbol> properties,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var property in properties)
+ {
+ // TODO: We should still restore read-only properties after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
+ if (property.IsWriteOnly || property.IsReadOnly)
+ continue;
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(property.Type, typeCache);
+
+ if (marshalType == null)
+ continue;
+
+ yield return new GodotPropertyData(property, marshalType.Value);
+ }
+ }
+
+ public static IEnumerable<GodotFieldData> WhereIsGodotCompatibleType(
+ this IEnumerable<IFieldSymbol> fields,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var field in fields)
+ {
+ // TODO: We should still restore read-only fields after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
+ if (field.IsReadOnly)
+ continue;
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(field.Type, typeCache);
+
+ if (marshalType == null)
+ continue;
+
+ yield return new GodotFieldData(field, marshalType.Value);
+ }
+ }
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
index 11d8e0f72b..f51b5970c3 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
- <LangVersion>8.0</LangVersion>
+ <LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
@@ -21,21 +21,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
- <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.1" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<!-- Package the generator in the analyzer directory of the nuget package -->
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<!-- Package the props file -->
- <None Include="Godot.SourceGenerators.props" Pack="true" PackagePath="build" Visible="false" />
+ <None Include="Godot.SourceGenerators.props" Pack="true" PackagePath="build" Visible="true" />
</ItemGroup>
-
- <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
- <PropertyGroup>
- <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
- <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
- </PropertyGroup>
- <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
- </Target>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
index f9b47ad5b1..7881ed0a8c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
@@ -2,6 +2,7 @@
<ItemGroup>
<!-- $(GodotProjectDir) is defined by Godot.NET.Sdk -->
<CompilerVisibleProperty Include="GodotProjectDir" />
- <CompilerVisibleProperty Include="GodotScriptPathAttributeGenerator" />
+ <CompilerVisibleProperty Include="GodotSourceGenerators" />
+ <CompilerVisibleProperty Include="IsGodotToolsProject" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
index 29e41d155a..1d8ddbabf2 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
@@ -3,7 +3,14 @@ namespace Godot.SourceGenerators
public static class GodotClasses
{
public const string Object = "Godot.Object";
- public const string DisableGodotGeneratorsAttr = "Godot.DisableGodotGeneratorsAttribute";
public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
+ public const string ExportAttr = "Godot.ExportAttribute";
+ public const string ExportCategoryAttr = "Godot.ExportCategoryAttribute";
+ public const string ExportGroupAttr = "Godot.ExportGroupAttribute";
+ public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute";
+ public const string SignalAttr = "Godot.SignalAttribute";
+ public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute";
+ public const string GodotClassNameAttr = "Godot.GodotClassName";
+ public const string SystemFlagsAttr = "System.FlagsAttribute";
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
new file mode 100644
index 0000000000..1a25d684a0
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
@@ -0,0 +1,148 @@
+using System;
+
+namespace Godot.SourceGenerators
+{
+ // TODO: May need to think about compatibility here. Could Godot change these values between minor versions?
+
+ internal enum VariantType
+ {
+ Nil = 0,
+ Bool = 1,
+ Int = 2,
+ Float = 3,
+ String = 4,
+ Vector2 = 5,
+ Vector2i = 6,
+ Rect2 = 7,
+ Rect2i = 8,
+ Vector3 = 9,
+ Vector3i = 10,
+ Transform2d = 11,
+ Vector4 = 12,
+ Vector4i = 13,
+ Plane = 14,
+ Quaternion = 15,
+ Aabb = 16,
+ Basis = 17,
+ Transform3d = 18,
+ Projection = 19,
+ Color = 20,
+ StringName = 21,
+ NodePath = 22,
+ Rid = 23,
+ Object = 24,
+ Callable = 25,
+ Signal = 26,
+ Dictionary = 27,
+ Array = 28,
+ PackedByteArray = 29,
+ PackedInt32Array = 30,
+ PackedInt64Array = 31,
+ PackedFloat32Array = 32,
+ PackedFloat64Array = 33,
+ PackedStringArray = 34,
+ PackedVector2Array = 35,
+ PackedVector3Array = 36,
+ PackedColorArray = 37,
+ Max = 38
+ }
+
+ internal enum PropertyHint
+ {
+ None = 0,
+ Range = 1,
+ Enum = 2,
+ EnumSuggestion = 3,
+ ExpEasing = 4,
+ Link = 5,
+ Flags = 6,
+ Layers2dRender = 7,
+ Layers2dPhysics = 8,
+ Layers2dNavigation = 9,
+ Layers3dRender = 10,
+ Layers3dPhysics = 11,
+ Layers3dNavigation = 12,
+ File = 13,
+ Dir = 14,
+ GlobalFile = 15,
+ GlobalDir = 16,
+ ResourceType = 17,
+ MultilineText = 18,
+ Expression = 19,
+ PlaceholderText = 20,
+ ColorNoAlpha = 21,
+ ImageCompressLossy = 22,
+ ImageCompressLossless = 23,
+ ObjectId = 24,
+ TypeString = 25,
+ NodePathToEditedNode = 26,
+ MethodOfVariantType = 27,
+ MethodOfBaseType = 28,
+ MethodOfInstance = 29,
+ MethodOfScript = 30,
+ PropertyOfVariantType = 31,
+ PropertyOfBaseType = 32,
+ PropertyOfInstance = 33,
+ PropertyOfScript = 34,
+ ObjectTooBig = 35,
+ NodePathValidTypes = 36,
+ SaveFile = 37,
+ GlobalSaveFile = 38,
+ IntIsObjectid = 39,
+ IntIsPointer = 41,
+ ArrayType = 40,
+ LocaleId = 42,
+ LocalizableString = 43,
+ NodeType = 44,
+ Max = 45
+ }
+
+ [Flags]
+ internal enum PropertyUsageFlags
+ {
+ None = 0,
+ Storage = 2,
+ Editor = 4,
+ Checkable = 8,
+ Checked = 16,
+ Internationalized = 32,
+ Group = 64,
+ Category = 128,
+ Subgroup = 256,
+ ClassIsBitfield = 512,
+ NoInstanceState = 1024,
+ RestartIfChanged = 2048,
+ ScriptVariable = 4096,
+ StoreIfNull = 8192,
+ AnimateAsTrigger = 16384,
+ UpdateAllIfModified = 32768,
+ ScriptDefaultValue = 65536,
+ ClassIsEnum = 131072,
+ NilIsVariant = 262144,
+ Internal = 524288,
+ DoNotShareOnDuplicate = 1048576,
+ HighEndGfx = 2097152,
+ NodePathFromSceneRoot = 4194304,
+ ResourceNotPersistent = 8388608,
+ KeyingIncrements = 16777216,
+ DeferredSetResource = 33554432,
+ EditorInstantiateObject = 67108864,
+ EditorBasicSetting = 134217728,
+ Array = 536870912,
+ Default = 6,
+ DefaultIntl = 38,
+ NoEditor = 2
+ }
+
+ public enum MethodFlags
+ {
+ Normal = 1,
+ Editor = 2,
+ Const = 4,
+ Virtual = 8,
+ Vararg = 16,
+ Static = 32,
+ ObjectCore = 64,
+ Default = 1
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
new file mode 100644
index 0000000000..a3ad8cbabd
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
@@ -0,0 +1,62 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ public struct GodotMethodData
+ {
+ public GodotMethodData(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes,
+ ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType, ITypeSymbol? retSymbol)
+ {
+ Method = method;
+ ParamTypes = paramTypes;
+ ParamTypeSymbols = paramTypeSymbols;
+ RetType = retType;
+ RetSymbol = retSymbol;
+ }
+
+ public IMethodSymbol Method { get; }
+ public ImmutableArray<MarshalType> ParamTypes { get; }
+ public ImmutableArray<ITypeSymbol> ParamTypeSymbols { get; }
+ public MarshalType? RetType { get; }
+ public ITypeSymbol? RetSymbol { get; }
+ }
+
+ public struct GodotSignalDelegateData
+ {
+ public GodotSignalDelegateData(string name, INamedTypeSymbol delegateSymbol, GodotMethodData invokeMethodData)
+ {
+ Name = name;
+ DelegateSymbol = delegateSymbol;
+ InvokeMethodData = invokeMethodData;
+ }
+
+ public string Name { get; }
+ public INamedTypeSymbol DelegateSymbol { get; }
+ public GodotMethodData InvokeMethodData { get; }
+ }
+
+ public struct GodotPropertyData
+ {
+ public GodotPropertyData(IPropertySymbol propertySymbol, MarshalType type)
+ {
+ PropertySymbol = propertySymbol;
+ Type = type;
+ }
+
+ public IPropertySymbol PropertySymbol { get; }
+ public MarshalType Type { get; }
+ }
+
+ public struct GodotFieldData
+ {
+ public GodotFieldData(IFieldSymbol fieldSymbol, MarshalType type)
+ {
+ FieldSymbol = fieldSymbol;
+ Type = type;
+ }
+
+ public IFieldSymbol FieldSymbol { get; }
+ public MarshalType Type { get; }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs
new file mode 100644
index 0000000000..54da6218f3
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs
@@ -0,0 +1,60 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class GodotPluginsInitializerGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.IsGodotToolsProject())
+ return;
+
+ string source =
+ @"using System;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+namespace GodotPlugins.Game
+{
+ internal static partial class Main
+ {
+ [UnmanagedCallersOnly(EntryPoint = ""godotsharp_game_main_init"")]
+ private static godot_bool InitializeFromGameProject(IntPtr godotDllHandle, IntPtr outManagedCallbacks)
+ {
+ try
+ {
+ DllImportResolver dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
+
+ var coreApiAssembly = typeof(Godot.Object).Assembly;
+
+ NativeLibrary.SetDllImportResolver(coreApiAssembly, dllImportResolver);
+
+ ManagedCallbacks.Create(outManagedCallbacks);
+
+ ScriptManagerBridge.LookupScriptsInAssembly(typeof(GodotPlugins.Game.Main).Assembly);
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return false.ToGodotBool();
+ }
+ }
+ }
+}
+";
+
+ context.AddSource("GodotPlugins.Game_Generated",
+ SourceText.From(source, Encoding.UTF8));
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
new file mode 100644
index 0000000000..15f5803bf0
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
@@ -0,0 +1,73 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum MarshalType
+ {
+ Boolean,
+ Char,
+ SByte,
+ Int16,
+ Int32,
+ Int64,
+ Byte,
+ UInt16,
+ UInt32,
+ UInt64,
+ Single,
+ Double,
+ String,
+
+ // Godot structs
+ Vector2,
+ Vector2i,
+ Rect2,
+ Rect2i,
+ Transform2D,
+ Vector3,
+ Vector3i,
+ Basis,
+ Quaternion,
+ Transform3D,
+ Vector4,
+ Vector4i,
+ Projection,
+ AABB,
+ Color,
+ Plane,
+ Callable,
+ SignalInfo,
+
+ // Enums
+ Enum,
+
+ // Arrays
+ ByteArray,
+ Int32Array,
+ Int64Array,
+ Float32Array,
+ Float64Array,
+ StringArray,
+ Vector2Array,
+ Vector3Array,
+ ColorArray,
+ GodotObjectOrDerivedArray,
+ SystemArrayOfStringName,
+ SystemArrayOfNodePath,
+ SystemArrayOfRID,
+
+ // Variant
+ Variant,
+
+ // Classes
+ GodotObjectOrDerived,
+ StringName,
+ NodePath,
+ RID,
+ GodotDictionary,
+ GodotArray,
+ GodotGenericDictionary,
+ GodotGenericArray,
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
new file mode 100644
index 0000000000..831ac3bdeb
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -0,0 +1,678 @@
+using System;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ internal static class MarshalUtils
+ {
+ public class TypeCache
+ {
+ public INamedTypeSymbol GodotObjectType { get; }
+
+ public TypeCache(Compilation compilation)
+ {
+ INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
+ {
+ return compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
+ throw new InvalidOperationException("Type not found: " + fullyQualifiedMetadataName);
+ }
+
+ GodotObjectType = GetTypeByMetadataNameOrThrow("Godot.Object");
+ }
+ }
+
+ public static VariantType? ConvertMarshalTypeToVariantType(MarshalType marshalType)
+ => marshalType switch
+ {
+ MarshalType.Boolean => VariantType.Bool,
+ MarshalType.Char => VariantType.Int,
+ MarshalType.SByte => VariantType.Int,
+ MarshalType.Int16 => VariantType.Int,
+ MarshalType.Int32 => VariantType.Int,
+ MarshalType.Int64 => VariantType.Int,
+ MarshalType.Byte => VariantType.Int,
+ MarshalType.UInt16 => VariantType.Int,
+ MarshalType.UInt32 => VariantType.Int,
+ MarshalType.UInt64 => VariantType.Int,
+ MarshalType.Single => VariantType.Float,
+ MarshalType.Double => VariantType.Float,
+ MarshalType.String => VariantType.String,
+ MarshalType.Vector2 => VariantType.Vector2,
+ MarshalType.Vector2i => VariantType.Vector2i,
+ MarshalType.Rect2 => VariantType.Rect2,
+ MarshalType.Rect2i => VariantType.Rect2i,
+ MarshalType.Transform2D => VariantType.Transform2d,
+ MarshalType.Vector3 => VariantType.Vector3,
+ MarshalType.Vector3i => VariantType.Vector3i,
+ MarshalType.Basis => VariantType.Basis,
+ MarshalType.Quaternion => VariantType.Quaternion,
+ MarshalType.Transform3D => VariantType.Transform3d,
+ MarshalType.Vector4 => VariantType.Vector4,
+ MarshalType.Vector4i => VariantType.Vector4i,
+ MarshalType.Projection => VariantType.Projection,
+ MarshalType.AABB => VariantType.Aabb,
+ MarshalType.Color => VariantType.Color,
+ MarshalType.Plane => VariantType.Plane,
+ MarshalType.Callable => VariantType.Callable,
+ MarshalType.SignalInfo => VariantType.Signal,
+ MarshalType.Enum => VariantType.Int,
+ MarshalType.ByteArray => VariantType.PackedByteArray,
+ MarshalType.Int32Array => VariantType.PackedInt32Array,
+ MarshalType.Int64Array => VariantType.PackedInt64Array,
+ MarshalType.Float32Array => VariantType.PackedFloat32Array,
+ MarshalType.Float64Array => VariantType.PackedFloat64Array,
+ MarshalType.StringArray => VariantType.PackedStringArray,
+ MarshalType.Vector2Array => VariantType.PackedVector2Array,
+ MarshalType.Vector3Array => VariantType.PackedVector3Array,
+ MarshalType.ColorArray => VariantType.PackedColorArray,
+ MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
+ MarshalType.SystemArrayOfStringName => VariantType.Array,
+ MarshalType.SystemArrayOfNodePath => VariantType.Array,
+ MarshalType.SystemArrayOfRID => VariantType.Array,
+ MarshalType.Variant => VariantType.Nil,
+ MarshalType.GodotObjectOrDerived => VariantType.Object,
+ MarshalType.StringName => VariantType.StringName,
+ MarshalType.NodePath => VariantType.NodePath,
+ MarshalType.RID => VariantType.Rid,
+ MarshalType.GodotDictionary => VariantType.Dictionary,
+ MarshalType.GodotArray => VariantType.Array,
+ MarshalType.GodotGenericDictionary => VariantType.Dictionary,
+ MarshalType.GodotGenericArray => VariantType.Array,
+ _ => null
+ };
+
+ public static MarshalType? ConvertManagedTypeToMarshalType(ITypeSymbol type, TypeCache typeCache)
+ {
+ var specialType = type.SpecialType;
+
+ switch (specialType)
+ {
+ case SpecialType.System_Boolean:
+ return MarshalType.Boolean;
+ case SpecialType.System_Char:
+ return MarshalType.Char;
+ case SpecialType.System_SByte:
+ return MarshalType.SByte;
+ case SpecialType.System_Int16:
+ return MarshalType.Int16;
+ case SpecialType.System_Int32:
+ return MarshalType.Int32;
+ case SpecialType.System_Int64:
+ return MarshalType.Int64;
+ case SpecialType.System_Byte:
+ return MarshalType.Byte;
+ case SpecialType.System_UInt16:
+ return MarshalType.UInt16;
+ case SpecialType.System_UInt32:
+ return MarshalType.UInt32;
+ case SpecialType.System_UInt64:
+ return MarshalType.UInt64;
+ case SpecialType.System_Single:
+ return MarshalType.Single;
+ case SpecialType.System_Double:
+ return MarshalType.Double;
+ case SpecialType.System_String:
+ return MarshalType.String;
+ default:
+ {
+ var typeKind = type.TypeKind;
+
+ if (typeKind == TypeKind.Enum)
+ return MarshalType.Enum;
+
+ if (typeKind == TypeKind.Struct)
+ {
+ if (type.ContainingAssembly.Name == "GodotSharp" &&
+ type.ContainingNamespace.Name == "Godot")
+ {
+ return type switch
+ {
+ { Name: "Vector2" } => MarshalType.Vector2,
+ { Name: "Vector2i" } => MarshalType.Vector2i,
+ { Name: "Rect2" } => MarshalType.Rect2,
+ { Name: "Rect2i" } => MarshalType.Rect2i,
+ { Name: "Transform2D" } => MarshalType.Transform2D,
+ { Name: "Vector3" } => MarshalType.Vector3,
+ { Name: "Vector3i" } => MarshalType.Vector3i,
+ { Name: "Basis" } => MarshalType.Basis,
+ { Name: "Quaternion" } => MarshalType.Quaternion,
+ { Name: "Transform3D" } => MarshalType.Transform3D,
+ { Name: "Vector4" } => MarshalType.Vector4,
+ { Name: "Vector4i" } => MarshalType.Vector4i,
+ { Name: "Projection" } => MarshalType.Projection,
+ { Name: "AABB" } => MarshalType.AABB,
+ { Name: "Color" } => MarshalType.Color,
+ { Name: "Plane" } => MarshalType.Plane,
+ { Name: "RID" } => MarshalType.RID,
+ { Name: "Callable" } => MarshalType.Callable,
+ { Name: "SignalInfo" } => MarshalType.SignalInfo,
+ { Name: "Variant" } => MarshalType.Variant,
+ _ => null
+ };
+ }
+ }
+ else if (typeKind == TypeKind.Array)
+ {
+ var arrayType = (IArrayTypeSymbol)type;
+ var elementType = arrayType.ElementType;
+
+ switch (elementType.SpecialType)
+ {
+ case SpecialType.System_Byte:
+ return MarshalType.ByteArray;
+ case SpecialType.System_Int32:
+ return MarshalType.Int32Array;
+ case SpecialType.System_Int64:
+ return MarshalType.Int64Array;
+ case SpecialType.System_Single:
+ return MarshalType.Float32Array;
+ case SpecialType.System_Double:
+ return MarshalType.Float64Array;
+ case SpecialType.System_String:
+ return MarshalType.StringArray;
+ }
+
+ if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
+ return MarshalType.GodotObjectOrDerivedArray;
+
+ if (elementType.ContainingAssembly.Name == "GodotSharp" &&
+ elementType.ContainingNamespace.Name == "Godot")
+ {
+ switch (elementType)
+ {
+ case { Name: "Vector2" }:
+ return MarshalType.Vector2Array;
+ case { Name: "Vector3" }:
+ return MarshalType.Vector3Array;
+ case { Name: "Color" }:
+ return MarshalType.ColorArray;
+ case { Name: "StringName" }:
+ return MarshalType.SystemArrayOfStringName;
+ case { Name: "NodePath" }:
+ return MarshalType.SystemArrayOfNodePath;
+ case { Name: "RID" }:
+ return MarshalType.SystemArrayOfRID;
+ }
+ }
+
+ return null;
+ }
+ else
+ {
+ if (type.SimpleDerivesFrom(typeCache.GodotObjectType))
+ return MarshalType.GodotObjectOrDerived;
+
+ if (type.ContainingAssembly.Name == "GodotSharp")
+ {
+ switch (type.ContainingNamespace.Name)
+ {
+ case "Godot":
+ return type switch
+ {
+ { Name: "StringName" } => MarshalType.StringName,
+ { Name: "NodePath" } => MarshalType.NodePath,
+ _ => null
+ };
+ case "Collections"
+ when type.ContainingNamespace.FullQualifiedName() == "Godot.Collections":
+ return type switch
+ {
+ { Name: "Dictionary" } =>
+ type is INamedTypeSymbol { IsGenericType: false } ?
+ MarshalType.GodotDictionary :
+ MarshalType.GodotGenericDictionary,
+ { Name: "Array" } =>
+ type is INamedTypeSymbol { IsGenericType: false } ?
+ MarshalType.GodotArray :
+ MarshalType.GodotGenericArray,
+ _ => null
+ };
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ private static bool SimpleDerivesFrom(this ITypeSymbol? type, ITypeSymbol candidateBaseType)
+ {
+ while (type != null)
+ {
+ if (SymbolEqualityComparer.Default.Equals(type, candidateBaseType))
+ return true;
+
+ type = type.BaseType;
+ }
+
+ return false;
+ }
+
+ public static ITypeSymbol? GetArrayElementType(ITypeSymbol typeSymbol)
+ {
+ if (typeSymbol.TypeKind == TypeKind.Array)
+ {
+ var arrayType = (IArrayTypeSymbol)typeSymbol;
+ return arrayType.ElementType;
+ }
+
+ if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType)
+ return genericType.TypeArguments.FirstOrDefault();
+
+ return null;
+ }
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b)
+ => source.Append(a).Append(b);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b, string c)
+ => source.Append(a).Append(b).Append(c);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d)
+ => source.Append(a).Append(b).Append(c).Append(d);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e, string f)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e, string f, string g)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e, string f, string g, string h)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g).Append(h);
+
+ private const string VariantUtils = "global::Godot.NativeInterop.VariantUtils";
+
+ public static StringBuilder AppendNativeVariantToManagedExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
+ {
+ return marshalType switch
+ {
+ MarshalType.Boolean =>
+ source.Append(VariantUtils, ".ConvertToBool(", inputExpr, ")"),
+ MarshalType.Char =>
+ source.Append("(char)", VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
+ MarshalType.SByte =>
+ source.Append(VariantUtils, ".ConvertToInt8(", inputExpr, ")"),
+ MarshalType.Int16 =>
+ source.Append(VariantUtils, ".ConvertToInt16(", inputExpr, ")"),
+ MarshalType.Int32 =>
+ source.Append(VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
+ MarshalType.Int64 =>
+ source.Append(VariantUtils, ".ConvertToInt64(", inputExpr, ")"),
+ MarshalType.Byte =>
+ source.Append(VariantUtils, ".ConvertToUInt8(", inputExpr, ")"),
+ MarshalType.UInt16 =>
+ source.Append(VariantUtils, ".ConvertToUInt16(", inputExpr, ")"),
+ MarshalType.UInt32 =>
+ source.Append(VariantUtils, ".ConvertToUInt32(", inputExpr, ")"),
+ MarshalType.UInt64 =>
+ source.Append(VariantUtils, ".ConvertToUInt64(", inputExpr, ")"),
+ MarshalType.Single =>
+ source.Append(VariantUtils, ".ConvertToFloat32(", inputExpr, ")"),
+ MarshalType.Double =>
+ source.Append(VariantUtils, ".ConvertToFloat64(", inputExpr, ")"),
+ MarshalType.String =>
+ source.Append(VariantUtils, ".ConvertToStringObject(", inputExpr, ")"),
+ MarshalType.Vector2 =>
+ source.Append(VariantUtils, ".ConvertToVector2(", inputExpr, ")"),
+ MarshalType.Vector2i =>
+ source.Append(VariantUtils, ".ConvertToVector2i(", inputExpr, ")"),
+ MarshalType.Rect2 =>
+ source.Append(VariantUtils, ".ConvertToRect2(", inputExpr, ")"),
+ MarshalType.Rect2i =>
+ source.Append(VariantUtils, ".ConvertToRect2i(", inputExpr, ")"),
+ MarshalType.Transform2D =>
+ source.Append(VariantUtils, ".ConvertToTransform2D(", inputExpr, ")"),
+ MarshalType.Vector3 =>
+ source.Append(VariantUtils, ".ConvertToVector3(", inputExpr, ")"),
+ MarshalType.Vector3i =>
+ source.Append(VariantUtils, ".ConvertToVector3i(", inputExpr, ")"),
+ MarshalType.Basis =>
+ source.Append(VariantUtils, ".ConvertToBasis(", inputExpr, ")"),
+ MarshalType.Quaternion =>
+ source.Append(VariantUtils, ".ConvertToQuaternion(", inputExpr, ")"),
+ MarshalType.Transform3D =>
+ source.Append(VariantUtils, ".ConvertToTransform3D(", inputExpr, ")"),
+ MarshalType.Vector4 =>
+ source.Append(VariantUtils, ".ConvertToVector4(", inputExpr, ")"),
+ MarshalType.Vector4i =>
+ source.Append(VariantUtils, ".ConvertToVector4i(", inputExpr, ")"),
+ MarshalType.Projection =>
+ source.Append(VariantUtils, ".ConvertToProjection(", inputExpr, ")"),
+ MarshalType.AABB =>
+ source.Append(VariantUtils, ".ConvertToAABB(", inputExpr, ")"),
+ MarshalType.Color =>
+ source.Append(VariantUtils, ".ConvertToColor(", inputExpr, ")"),
+ MarshalType.Plane =>
+ source.Append(VariantUtils, ".ConvertToPlane(", inputExpr, ")"),
+ MarshalType.Callable =>
+ source.Append(VariantUtils, ".ConvertToCallableManaged(", inputExpr, ")"),
+ MarshalType.SignalInfo =>
+ source.Append(VariantUtils, ".ConvertToSignalInfo(", inputExpr, ")"),
+ MarshalType.Enum =>
+ source.Append("(", typeSymbol.FullQualifiedName(),
+ ")", VariantUtils, ".ConvertToInt32(", inputExpr, ")"),
+ MarshalType.ByteArray =>
+ source.Append(VariantUtils, ".ConvertAsPackedByteArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.Int32Array =>
+ source.Append(VariantUtils, ".ConvertAsPackedInt32ArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.Int64Array =>
+ source.Append(VariantUtils, ".ConvertAsPackedInt64ArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.Float32Array =>
+ source.Append(VariantUtils, ".ConvertAsPackedFloat32ArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.Float64Array =>
+ source.Append(VariantUtils, ".ConvertAsPackedFloat64ArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.StringArray =>
+ source.Append(VariantUtils, ".ConvertAsPackedStringArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.Vector2Array =>
+ source.Append(VariantUtils, ".ConvertAsPackedVector2ArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.Vector3Array =>
+ source.Append(VariantUtils, ".ConvertAsPackedVector3ArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.ColorArray =>
+ source.Append(VariantUtils, ".ConvertAsPackedColorArrayToSystemArray(", inputExpr, ")"),
+ MarshalType.GodotObjectOrDerivedArray =>
+ source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
+ ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedName(), ">(", inputExpr, ")"),
+ MarshalType.SystemArrayOfStringName =>
+ source.Append(VariantUtils, ".ConvertToSystemArrayOfStringName(", inputExpr, ")"),
+ MarshalType.SystemArrayOfNodePath =>
+ source.Append(VariantUtils, ".ConvertToSystemArrayOfNodePath(", inputExpr, ")"),
+ MarshalType.SystemArrayOfRID =>
+ source.Append(VariantUtils, ".ConvertToSystemArrayOfRID(", inputExpr, ")"),
+ MarshalType.Variant =>
+ source.Append("global::Godot.Variant.CreateCopyingBorrowed(", inputExpr, ")"),
+ MarshalType.GodotObjectOrDerived =>
+ source.Append("(", typeSymbol.FullQualifiedName(),
+ ")", VariantUtils, ".ConvertToGodotObject(", inputExpr, ")"),
+ MarshalType.StringName =>
+ source.Append(VariantUtils, ".ConvertToStringNameObject(", inputExpr, ")"),
+ MarshalType.NodePath =>
+ source.Append(VariantUtils, ".ConvertToNodePathObject(", inputExpr, ")"),
+ MarshalType.RID =>
+ source.Append(VariantUtils, ".ConvertToRID(", inputExpr, ")"),
+ MarshalType.GodotDictionary =>
+ source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"),
+ MarshalType.GodotArray =>
+ source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"),
+ MarshalType.GodotGenericDictionary =>
+ source.Append(VariantUtils, ".ConvertToDictionaryObject<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ", ",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedName(), ">(", inputExpr, ")"),
+ MarshalType.GodotGenericArray =>
+ source.Append(VariantUtils, ".ConvertToArrayObject<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">(", inputExpr, ")"),
+ _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
+ "Received unexpected marshal type")
+ };
+ }
+
+ public static StringBuilder AppendManagedToNativeVariantExpr(
+ this StringBuilder source, string inputExpr, MarshalType marshalType)
+ {
+ return marshalType switch
+ {
+ MarshalType.Boolean =>
+ source.Append(VariantUtils, ".CreateFromBool(", inputExpr, ")"),
+ MarshalType.Char =>
+ source.Append(VariantUtils, ".CreateFromInt((ushort)", inputExpr, ")"),
+ MarshalType.SByte =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.Int16 =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.Int32 =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.Int64 =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.Byte =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.UInt16 =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.UInt32 =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.UInt64 =>
+ source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"),
+ MarshalType.Single =>
+ source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
+ MarshalType.Double =>
+ source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"),
+ MarshalType.String =>
+ source.Append(VariantUtils, ".CreateFromString(", inputExpr, ")"),
+ MarshalType.Vector2 =>
+ source.Append(VariantUtils, ".CreateFromVector2(", inputExpr, ")"),
+ MarshalType.Vector2i =>
+ source.Append(VariantUtils, ".CreateFromVector2i(", inputExpr, ")"),
+ MarshalType.Rect2 =>
+ source.Append(VariantUtils, ".CreateFromRect2(", inputExpr, ")"),
+ MarshalType.Rect2i =>
+ source.Append(VariantUtils, ".CreateFromRect2i(", inputExpr, ")"),
+ MarshalType.Transform2D =>
+ source.Append(VariantUtils, ".CreateFromTransform2D(", inputExpr, ")"),
+ MarshalType.Vector3 =>
+ source.Append(VariantUtils, ".CreateFromVector3(", inputExpr, ")"),
+ MarshalType.Vector3i =>
+ source.Append(VariantUtils, ".CreateFromVector3i(", inputExpr, ")"),
+ MarshalType.Basis =>
+ source.Append(VariantUtils, ".CreateFromBasis(", inputExpr, ")"),
+ MarshalType.Quaternion =>
+ source.Append(VariantUtils, ".CreateFromQuaternion(", inputExpr, ")"),
+ MarshalType.Transform3D =>
+ source.Append(VariantUtils, ".CreateFromTransform3D(", inputExpr, ")"),
+ MarshalType.Vector4 =>
+ source.Append(VariantUtils, ".CreateFromVector4(", inputExpr, ")"),
+ MarshalType.Vector4i =>
+ source.Append(VariantUtils, ".CreateFromVector4i(", inputExpr, ")"),
+ MarshalType.Projection =>
+ source.Append(VariantUtils, ".CreateFromProjection(", inputExpr, ")"),
+ MarshalType.AABB =>
+ source.Append(VariantUtils, ".CreateFromAABB(", inputExpr, ")"),
+ MarshalType.Color =>
+ source.Append(VariantUtils, ".CreateFromColor(", inputExpr, ")"),
+ MarshalType.Plane =>
+ source.Append(VariantUtils, ".CreateFromPlane(", inputExpr, ")"),
+ MarshalType.Callable =>
+ source.Append(VariantUtils, ".CreateFromCallable(", inputExpr, ")"),
+ MarshalType.SignalInfo =>
+ source.Append(VariantUtils, ".CreateFromSignalInfo(", inputExpr, ")"),
+ MarshalType.Enum =>
+ source.Append(VariantUtils, ".CreateFromInt((int)", inputExpr, ")"),
+ MarshalType.ByteArray =>
+ source.Append(VariantUtils, ".CreateFromPackedByteArray(", inputExpr, ")"),
+ MarshalType.Int32Array =>
+ source.Append(VariantUtils, ".CreateFromPackedInt32Array(", inputExpr, ")"),
+ MarshalType.Int64Array =>
+ source.Append(VariantUtils, ".CreateFromPackedInt64Array(", inputExpr, ")"),
+ MarshalType.Float32Array =>
+ source.Append(VariantUtils, ".CreateFromPackedFloat32Array(", inputExpr, ")"),
+ MarshalType.Float64Array =>
+ source.Append(VariantUtils, ".CreateFromPackedFloat64Array(", inputExpr, ")"),
+ MarshalType.StringArray =>
+ source.Append(VariantUtils, ".CreateFromPackedStringArray(", inputExpr, ")"),
+ MarshalType.Vector2Array =>
+ source.Append(VariantUtils, ".CreateFromPackedVector2Array(", inputExpr, ")"),
+ MarshalType.Vector3Array =>
+ source.Append(VariantUtils, ".CreateFromPackedVector3Array(", inputExpr, ")"),
+ MarshalType.ColorArray =>
+ source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"),
+ MarshalType.GodotObjectOrDerivedArray =>
+ source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"),
+ MarshalType.SystemArrayOfStringName =>
+ source.Append(VariantUtils, ".CreateFromSystemArrayOfStringName(", inputExpr, ")"),
+ MarshalType.SystemArrayOfNodePath =>
+ source.Append(VariantUtils, ".CreateFromSystemArrayOfNodePath(", inputExpr, ")"),
+ MarshalType.SystemArrayOfRID =>
+ source.Append(VariantUtils, ".CreateFromSystemArrayOfRID(", inputExpr, ")"),
+ MarshalType.Variant =>
+ source.Append(inputExpr, ".CopyNativeVariant()"),
+ MarshalType.GodotObjectOrDerived =>
+ source.Append(VariantUtils, ".CreateFromGodotObject(", inputExpr, ")"),
+ MarshalType.StringName =>
+ source.Append(VariantUtils, ".CreateFromStringName(", inputExpr, ")"),
+ MarshalType.NodePath =>
+ source.Append(VariantUtils, ".CreateFromNodePath(", inputExpr, ")"),
+ MarshalType.RID =>
+ source.Append(VariantUtils, ".CreateFromRID(", inputExpr, ")"),
+ MarshalType.GodotDictionary =>
+ source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
+ MarshalType.GodotArray =>
+ source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
+ MarshalType.GodotGenericDictionary =>
+ source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
+ MarshalType.GodotGenericArray =>
+ source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
+ _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
+ "Received unexpected marshal type")
+ };
+ }
+
+ public static StringBuilder AppendVariantToManagedExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
+ {
+ return marshalType switch
+ {
+ MarshalType.Boolean => source.Append(inputExpr, ".AsBool()"),
+ MarshalType.Char => source.Append(inputExpr, ".AsChar()"),
+ MarshalType.SByte => source.Append(inputExpr, ".AsSByte()"),
+ MarshalType.Int16 => source.Append(inputExpr, ".AsInt16()"),
+ MarshalType.Int32 => source.Append(inputExpr, ".AsInt32()"),
+ MarshalType.Int64 => source.Append(inputExpr, ".AsInt64()"),
+ MarshalType.Byte => source.Append(inputExpr, ".AsByte()"),
+ MarshalType.UInt16 => source.Append(inputExpr, ".AsUInt16()"),
+ MarshalType.UInt32 => source.Append(inputExpr, ".AsUInt32()"),
+ MarshalType.UInt64 => source.Append(inputExpr, ".AsUInt64()"),
+ MarshalType.Single => source.Append(inputExpr, ".AsSingle()"),
+ MarshalType.Double => source.Append(inputExpr, ".AsDouble()"),
+ MarshalType.String => source.Append(inputExpr, ".AsString()"),
+ MarshalType.Vector2 => source.Append(inputExpr, ".AsVector2()"),
+ MarshalType.Vector2i => source.Append(inputExpr, ".AsVector2i()"),
+ MarshalType.Rect2 => source.Append(inputExpr, ".AsRect2()"),
+ MarshalType.Rect2i => source.Append(inputExpr, ".AsRect2i()"),
+ MarshalType.Transform2D => source.Append(inputExpr, ".AsTransform2D()"),
+ MarshalType.Vector3 => source.Append(inputExpr, ".AsVector3()"),
+ MarshalType.Vector3i => source.Append(inputExpr, ".AsVector3i()"),
+ MarshalType.Basis => source.Append(inputExpr, ".AsBasis()"),
+ MarshalType.Quaternion => source.Append(inputExpr, ".AsQuaternion()"),
+ MarshalType.Transform3D => source.Append(inputExpr, ".AsTransform3D()"),
+ MarshalType.Vector4 => source.Append(inputExpr, ".AsVector4()"),
+ MarshalType.Vector4i => source.Append(inputExpr, ".AsVector4i()"),
+ MarshalType.Projection => source.Append(inputExpr, ".AsProjection()"),
+ MarshalType.AABB => source.Append(inputExpr, ".AsAABB()"),
+ MarshalType.Color => source.Append(inputExpr, ".AsColor()"),
+ MarshalType.Plane => source.Append(inputExpr, ".AsPlane()"),
+ MarshalType.Callable => source.Append(inputExpr, ".AsCallable()"),
+ MarshalType.SignalInfo => source.Append(inputExpr, ".AsSignalInfo()"),
+ MarshalType.Enum =>
+ source.Append("(", typeSymbol.FullQualifiedName(), ")", inputExpr, ".AsInt64()"),
+ MarshalType.ByteArray => source.Append(inputExpr, ".AsByteArray()"),
+ MarshalType.Int32Array => source.Append(inputExpr, ".AsInt32Array()"),
+ MarshalType.Int64Array => source.Append(inputExpr, ".AsInt64Array()"),
+ MarshalType.Float32Array => source.Append(inputExpr, ".AsFloat32Array()"),
+ MarshalType.Float64Array => source.Append(inputExpr, ".AsFloat64Array()"),
+ MarshalType.StringArray => source.Append(inputExpr, ".AsStringArray()"),
+ MarshalType.Vector2Array => source.Append(inputExpr, ".AsVector2Array()"),
+ MarshalType.Vector3Array => source.Append(inputExpr, ".AsVector3Array()"),
+ MarshalType.ColorArray => source.Append(inputExpr, ".AsColorArray()"),
+ MarshalType.GodotObjectOrDerivedArray => source.Append(inputExpr, ".AsGodotObjectArray<",
+ ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedName(), ">()"),
+ MarshalType.SystemArrayOfStringName => source.Append(inputExpr, ".AsSystemArrayOfStringName()"),
+ MarshalType.SystemArrayOfNodePath => source.Append(inputExpr, ".AsSystemArrayOfNodePath()"),
+ MarshalType.SystemArrayOfRID => source.Append(inputExpr, ".AsSystemArrayOfRID()"),
+ MarshalType.Variant => source.Append(inputExpr),
+ MarshalType.GodotObjectOrDerived => source.Append("(",
+ typeSymbol.FullQualifiedName(), ")", inputExpr, ".AsGodotObject()"),
+ MarshalType.StringName => source.Append(inputExpr, ".AsStringName()"),
+ MarshalType.NodePath => source.Append(inputExpr, ".AsNodePath()"),
+ MarshalType.RID => source.Append(inputExpr, ".AsRID()"),
+ MarshalType.GodotDictionary => source.Append(inputExpr, ".AsGodotDictionary()"),
+ MarshalType.GodotArray => source.Append(inputExpr, ".AsGodotArray()"),
+ MarshalType.GodotGenericDictionary => source.Append(inputExpr, ".AsGodotDictionary<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ", ",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedName(), ">()"),
+ MarshalType.GodotGenericArray => source.Append(inputExpr, ".AsGodotArray<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">()"),
+ _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
+ "Received unexpected marshal type")
+ };
+ }
+
+ public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source,
+ string inputExpr, MarshalType marshalType)
+ {
+ switch (marshalType)
+ {
+ case MarshalType.Boolean:
+ case MarshalType.Char:
+ case MarshalType.SByte:
+ case MarshalType.Int16:
+ case MarshalType.Int32:
+ case MarshalType.Int64:
+ case MarshalType.Byte:
+ case MarshalType.UInt16:
+ case MarshalType.UInt32:
+ case MarshalType.UInt64:
+ case MarshalType.Single:
+ case MarshalType.Double:
+ case MarshalType.String:
+ case MarshalType.Vector2:
+ case MarshalType.Vector2i:
+ case MarshalType.Rect2:
+ case MarshalType.Rect2i:
+ case MarshalType.Transform2D:
+ case MarshalType.Vector3:
+ case MarshalType.Vector3i:
+ case MarshalType.Basis:
+ case MarshalType.Quaternion:
+ case MarshalType.Transform3D:
+ case MarshalType.Vector4:
+ case MarshalType.Vector4i:
+ case MarshalType.Projection:
+ case MarshalType.AABB:
+ case MarshalType.Color:
+ case MarshalType.Plane:
+ case MarshalType.Callable:
+ case MarshalType.SignalInfo:
+ case MarshalType.ByteArray:
+ case MarshalType.Int32Array:
+ case MarshalType.Int64Array:
+ case MarshalType.Float32Array:
+ case MarshalType.Float64Array:
+ case MarshalType.StringArray:
+ case MarshalType.Vector2Array:
+ case MarshalType.Vector3Array:
+ case MarshalType.ColorArray:
+ case MarshalType.GodotObjectOrDerivedArray:
+ case MarshalType.SystemArrayOfStringName:
+ case MarshalType.SystemArrayOfNodePath:
+ case MarshalType.SystemArrayOfRID:
+ case MarshalType.GodotObjectOrDerived:
+ case MarshalType.StringName:
+ case MarshalType.NodePath:
+ case MarshalType.RID:
+ case MarshalType.GodotDictionary:
+ case MarshalType.GodotArray:
+ case MarshalType.GodotGenericDictionary:
+ case MarshalType.GodotGenericArray:
+ return source.Append("Variant.CreateFrom(", inputExpr, ")");
+ case MarshalType.Enum:
+ return source.Append("Variant.CreateFrom((long)", inputExpr, ")");
+ case MarshalType.Variant:
+ return source.Append(inputExpr);
+ default:
+ throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
+ "Received unexpected marshal type");
+ }
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs
new file mode 100644
index 0000000000..81c6f2b7d5
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace Godot.SourceGenerators
+{
+ internal struct MethodInfo
+ {
+ public MethodInfo(string name, PropertyInfo returnVal, MethodFlags flags,
+ List<PropertyInfo>? arguments,
+ List<string?>? defaultArguments)
+ {
+ Name = name;
+ ReturnVal = returnVal;
+ Flags = flags;
+ Arguments = arguments;
+ DefaultArguments = defaultArguments;
+ }
+
+ public string Name { get; }
+ public PropertyInfo ReturnVal { get; }
+ public MethodFlags Flags { get; }
+ public List<PropertyInfo>? Arguments { get; }
+ public List<string?>? DefaultArguments { get; }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
new file mode 100644
index 0000000000..7aaadb27be
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
@@ -0,0 +1,100 @@
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Godot.SourceGenerators
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class MustBeVariantAnalyzer : DiagnosticAnalyzer
+ {
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
+ => ImmutableArray.Create(
+ Common.GenericTypeArgumentMustBeVariantRule,
+ Common.GenericTypeParameterMustBeVariantAnnotatedRule,
+ Common.TypeArgumentParentSymbolUnhandledRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.TypeArgumentList);
+ }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ var typeArgListSyntax = (TypeArgumentListSyntax)context.Node;
+
+ // Method invocation or variable declaration that contained the type arguments
+ var parentSyntax = context.Node.Parent;
+ Debug.Assert(parentSyntax != null);
+
+ var sm = context.SemanticModel;
+
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ for (int i = 0; i < typeArgListSyntax.Arguments.Count; i++)
+ {
+ var typeSyntax = typeArgListSyntax.Arguments[i];
+ var typeSymbol = sm.GetSymbolInfo(typeSyntax).Symbol as ITypeSymbol;
+ Debug.Assert(typeSymbol != null);
+
+ var parentSymbol = sm.GetSymbolInfo(parentSyntax).Symbol;
+
+ if (!ShouldCheckTypeArgument(context, parentSyntax, parentSymbol, typeSyntax, typeSymbol, i))
+ {
+ return;
+ }
+
+ if (typeSymbol is ITypeParameterSymbol typeParamSymbol)
+ {
+ if (!typeParamSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false))
+ {
+ Common.ReportGenericTypeParameterMustBeVariantAnnotated(context, typeSyntax, typeSymbol);
+ }
+ continue;
+ }
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(typeSymbol, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportGenericTypeArgumentMustBeVariant(context, typeSyntax, typeSymbol);
+ continue;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Check if the given type argument is being used in a type parameter that contains
+ /// the <c>MustBeVariantAttribute</c>; otherwise, we ignore the attribute.
+ /// </summary>
+ /// <param name="context">Context for a syntax node action.</param>
+ /// <param name="parentSyntax">The parent node syntax that contains the type node syntax.</param>
+ /// <param name="parentSymbol">The symbol retrieved for the parent node syntax.</param>
+ /// <param name="typeArgumentSyntax">The type node syntax of the argument type to check.</param>
+ /// <param name="typeArgumentSymbol">The symbol retrieved for the type node syntax.</param>
+ /// <returns><see langword="true"/> if the type must be variant and must be analyzed.</returns>
+ private bool ShouldCheckTypeArgument(SyntaxNodeAnalysisContext context, SyntaxNode parentSyntax, ISymbol parentSymbol, TypeSyntax typeArgumentSyntax, ITypeSymbol typeArgumentSymbol, int typeArgumentIndex)
+ {
+ var typeParamSymbol = parentSymbol switch
+ {
+ IMethodSymbol methodSymbol => methodSymbol.TypeParameters[typeArgumentIndex],
+ INamedTypeSymbol typeSymbol => typeSymbol.TypeParameters[typeArgumentIndex],
+ _ => null,
+ };
+
+ if (typeParamSymbol == null)
+ {
+ Common.ReportTypeArgumentParentSymbolUnhandled(context, typeArgumentSyntax, parentSymbol);
+ return false;
+ }
+
+ return typeParamSymbol.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false);
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs
new file mode 100644
index 0000000000..b345f5f84d
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs
@@ -0,0 +1,23 @@
+namespace Godot.SourceGenerators
+{
+ internal struct PropertyInfo
+ {
+ public PropertyInfo(VariantType type, string name, PropertyHint hint,
+ string? hintString, PropertyUsageFlags usage, bool exported)
+ {
+ Type = type;
+ Name = name;
+ Hint = hint;
+ HintString = hintString;
+ Usage = usage;
+ Exported = exported;
+ }
+
+ public VariantType Type { get; }
+ public string Name { get; }
+ public PropertyHint Hint { get; }
+ public string? HintString { get; }
+ public PropertyUsageFlags Usage { get; }
+ public bool Exported { get; }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
new file mode 100644
index 0000000000..5ac4f4a47e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -0,0 +1,408 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptMethodsGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private class MethodOverloadEqualityComparer : IEqualityComparer<GodotMethodData>
+ {
+ public bool Equals(GodotMethodData x, GodotMethodData y)
+ => x.ParamTypes.Length == y.ParamTypes.Length && x.Method.Name == y.Method.Name;
+
+ public int GetHashCode(GodotMethodData obj)
+ {
+ unchecked
+ {
+ return (obj.ParamTypes.Length.GetHashCode() * 397) ^ obj.Method.Name.GetHashCode();
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptMethods_Generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ var methodSymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Method && !s.IsImplicitlyDeclared)
+ .Cast<IMethodSymbol>()
+ .Where(m => m.MethodKind == MethodKind.Ordinary);
+
+ var godotClassMethods = methodSymbols.WhereHasGodotCompatibleSignature(typeCache)
+ .Distinct(new MethodOverloadEqualityComparer())
+ .ToArray();
+
+ source.Append(" private partial class GodotInternal {\n");
+
+ // Generate cached StringNames for methods and properties, for fast lookup
+
+ var distinctMethodNames = godotClassMethods
+ .Select(m => m.Method.Name)
+ .Distinct()
+ .ToArray();
+
+ foreach (string methodName in distinctMethodNames)
+ {
+ source.Append(" public static readonly StringName MethodName_");
+ source.Append(methodName);
+ source.Append(" = \"");
+ source.Append(methodName);
+ source.Append("\";\n");
+ }
+
+ source.Append(" }\n"); // class GodotInternal
+
+ // Generate GetGodotMethodList
+
+ if (godotClassMethods.Length > 0)
+ {
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ const string listType = "System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
+
+ source.Append(" internal new static ")
+ .Append(listType)
+ .Append(" GetGodotMethodList()\n {\n");
+
+ source.Append(" var methods = new ")
+ .Append(listType)
+ .Append("(")
+ .Append(godotClassMethods.Length)
+ .Append(");\n");
+
+ foreach (var method in godotClassMethods)
+ {
+ var methodInfo = DetermineMethodInfo(method);
+ AppendMethodInfo(source, methodInfo);
+ }
+
+ source.Append(" return methods;\n");
+ source.Append(" }\n");
+
+ source.Append("#pragma warning restore CS0109\n");
+ }
+
+ // Generate InvokeGodotClassMethod
+
+ if (godotClassMethods.Length > 0)
+ {
+ source.Append(" protected override bool InvokeGodotClassMethod(in godot_string_name method, ");
+ source.Append("NativeVariantPtrArgs args, int argCount, out godot_variant ret)\n {\n");
+
+ foreach (var method in godotClassMethods)
+ {
+ GenerateMethodInvoker(method, source);
+ }
+
+ source.Append(" return base.InvokeGodotClassMethod(method, args, argCount, out ret);\n");
+
+ source.Append(" }\n");
+ }
+
+ // Generate HasGodotClassMethod
+
+ if (distinctMethodNames.Length > 0)
+ {
+ source.Append(" protected override bool HasGodotClassMethod(in godot_string_name method)\n {\n");
+
+ bool isFirstEntry = true;
+ foreach (string methodName in distinctMethodNames)
+ {
+ GenerateHasMethodEntry(methodName, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.HasGodotClassMethod(method);\n");
+
+ source.Append(" }\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
+ {
+ source.Append(" methods.Add(new(name: GodotInternal.MethodName_")
+ .Append(methodInfo.Name)
+ .Append(", returnVal: ");
+
+ AppendPropertyInfo(source, methodInfo.ReturnVal);
+
+ source.Append(", flags: (Godot.MethodFlags)")
+ .Append((int)methodInfo.Flags)
+ .Append(", arguments: ");
+
+ if (methodInfo.Arguments is { Count: > 0 })
+ {
+ source.Append("new() { ");
+
+ foreach (var param in methodInfo.Arguments)
+ {
+ AppendPropertyInfo(source, param);
+
+ // C# allows colon after the last element
+ source.Append(", ");
+ }
+
+ source.Append(" }");
+ }
+ else
+ {
+ source.Append("null");
+ }
+
+ source.Append(", defaultArguments: null));\n");
+ }
+
+ private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append("new(type: (Godot.Variant.Type)")
+ .Append((int)propertyInfo.Type)
+ .Append(", name: \"")
+ .Append(propertyInfo.Name)
+ .Append("\", hint: (Godot.PropertyHint)")
+ .Append((int)propertyInfo.Hint)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: ")
+ .Append(propertyInfo.Exported ? "true" : "false")
+ .Append(")");
+ }
+
+ private static MethodInfo DetermineMethodInfo(GodotMethodData method)
+ {
+ PropertyInfo returnVal;
+
+ if (method.RetType != null)
+ {
+ returnVal = DeterminePropertyInfo(method.RetType.Value, name: string.Empty);
+ }
+ else
+ {
+ returnVal = new PropertyInfo(VariantType.Nil, string.Empty, PropertyHint.None,
+ hintString: null, PropertyUsageFlags.Default, exported: false);
+ }
+
+ int paramCount = method.ParamTypes.Length;
+
+ List<PropertyInfo>? arguments;
+
+ if (paramCount > 0)
+ {
+ arguments = new(capacity: paramCount);
+
+ for (int i = 0; i < paramCount; i++)
+ {
+ arguments.Add(DeterminePropertyInfo(method.ParamTypes[i],
+ name: method.Method.Parameters[i].Name));
+ }
+ }
+ else
+ {
+ arguments = null;
+ }
+
+ return new MethodInfo(method.Method.Name, returnVal, MethodFlags.Default, arguments,
+ defaultArguments: null);
+ }
+
+ private static PropertyInfo DeterminePropertyInfo(MarshalType marshalType, string name)
+ {
+ var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
+
+ var propUsage = PropertyUsageFlags.Default;
+
+ if (memberVariantType == VariantType.Nil)
+ propUsage |= PropertyUsageFlags.NilIsVariant;
+
+ return new PropertyInfo(memberVariantType, name,
+ PropertyHint.None, string.Empty, propUsage, exported: false);
+ }
+
+ private static void GenerateHasMethodEntry(
+ string methodName,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+ if (!isFirstEntry)
+ source.Append("else ");
+ source.Append("if (method == GodotInternal.MethodName_");
+ source.Append(methodName);
+ source.Append(") {\n return true;\n }\n");
+ }
+
+ private static void GenerateMethodInvoker(
+ GodotMethodData method,
+ StringBuilder source
+ )
+ {
+ string methodName = method.Method.Name;
+
+ source.Append(" if (method == GodotInternal.MethodName_");
+ source.Append(methodName);
+ source.Append(" && argCount == ");
+ source.Append(method.ParamTypes.Length);
+ source.Append(") {\n");
+
+ if (method.RetType != null)
+ source.Append(" var callRet = ");
+ else
+ source.Append(" ");
+
+ source.Append(methodName);
+ source.Append("(");
+
+ for (int i = 0; i < method.ParamTypes.Length; i++)
+ {
+ if (i != 0)
+ source.Append(", ");
+
+ source.AppendNativeVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
+ method.ParamTypeSymbols[i], method.ParamTypes[i]);
+ }
+
+ source.Append(");\n");
+
+ if (method.RetType != null)
+ {
+ source.Append(" ret = ");
+
+ source.AppendManagedToNativeVariantExpr("callRet", method.RetType.Value);
+ source.Append(";\n");
+
+ source.Append(" return true;\n");
+ }
+ else
+ {
+ source.Append(" ret = default;\n");
+ source.Append(" return true;\n");
+ }
+
+ source.Append(" }\n");
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
index fa65595290..e8a9e28d0c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -14,13 +14,13 @@ namespace Godot.SourceGenerators
{
public void Execute(GeneratorExecutionContext context)
{
- if (context.TryGetGlobalAnalyzerProperty("GodotScriptPathAttributeGenerator", out string? toggle)
- && toggle == "disabled")
- {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ if (context.IsGodotToolsProject())
return;
- }
- // NOTE: IsNullOrEmpty doesn't work well with nullable checks
+ // NOTE: NotNullWhen diagnostics don't work on projects targeting .NET Standard 2.0
// ReSharper disable once ReplaceWithStringIsNullOrEmpty
if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out string? godotProjectDir)
|| godotProjectDir!.Length == 0)
@@ -28,17 +28,18 @@ namespace Godot.SourceGenerators
throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
}
- var godotClasses = context.Compilation.SyntaxTrees
+ Dictionary<INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>> godotClasses = context
+ .Compilation.SyntaxTrees
.SelectMany(tree =>
tree.GetRoot().DescendantNodes()
.OfType<ClassDeclarationSyntax>()
// Ignore inner classes
- .Where(cds => !(cds.Parent is ClassDeclarationSyntax))
+ .Where(cds => !cds.IsNested())
.SelectGodotScriptClasses(context.Compilation)
// Report and skip non-partial classes
.Where(x =>
{
- if (x.cds.IsPartial() || x.symbol.HasDisableGeneratorsAttribute())
+ if (x.cds.IsPartial())
return true;
Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
return false;
@@ -89,21 +90,14 @@ namespace Godot.SourceGenerators
attributes.Append(@""")]");
}
- string className = symbol.Name;
-
INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
namespaceSymbol.FullQualifiedName() :
string.Empty;
bool hasNamespace = classNs.Length != 0;
- var uniqueName = new StringBuilder();
- if (hasNamespace)
- uniqueName.Append($"{classNs}.");
- uniqueName.Append(className);
- if (symbol.IsGenericType)
- uniqueName.Append($"Of{string.Join(string.Empty, symbol.TypeParameters)}");
- uniqueName.Append("_ScriptPath_Generated");
+ var uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptPath_Generated";
var source = new StringBuilder();
@@ -123,10 +117,8 @@ namespace Godot.SourceGenerators
}
source.Append(attributes);
- source.Append("\n partial class ");
- source.Append(className);
- if (symbol.IsGenericType)
- source.Append($"<{string.Join(", ", symbol.TypeParameters)}>");
+ source.Append("\npartial class ");
+ source.Append(symbol.NameWithTypeParameters());
source.Append("\n{\n}\n");
if (hasNamespace)
@@ -134,7 +126,7 @@ namespace Godot.SourceGenerators
source.Append("\n}\n");
}
- context.AddSource(uniqueName.ToString(), SourceText.From(source.ToString(), Encoding.UTF8));
+ context.AddSource(uniqueHint.ToString(), SourceText.From(source.ToString(), Encoding.UTF8));
}
private static void AddScriptTypesAssemblyAttr(GeneratorExecutionContext context,
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
new file mode 100644
index 0000000000..0c9b17d69a
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -0,0 +1,664 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptPropertiesGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptProperties_Generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ var propertySymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property)
+ .Cast<IPropertySymbol>();
+
+ var fieldSymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>();
+
+ var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+ var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+
+ source.Append(" private partial class GodotInternal {\n");
+
+ // Generate cached StringNames for methods and properties, for fast lookup
+
+ foreach (var property in godotClassProperties)
+ {
+ string propertyName = property.PropertySymbol.Name;
+ source.Append(" public static readonly StringName PropName_");
+ source.Append(propertyName);
+ source.Append(" = \"");
+ source.Append(propertyName);
+ source.Append("\";\n");
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ string fieldName = field.FieldSymbol.Name;
+ source.Append(" public static readonly StringName PropName_");
+ source.Append(fieldName);
+ source.Append(" = \"");
+ source.Append(fieldName);
+ source.Append("\";\n");
+ }
+
+ source.Append(" }\n"); // class GodotInternal
+
+ if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
+ {
+ bool isFirstEntry;
+
+ // Generate SetGodotClassPropertyValue
+
+ bool allPropertiesAreReadOnly = godotClassFields.All(fi => fi.FieldSymbol.IsReadOnly) &&
+ godotClassProperties.All(pi => pi.PropertySymbol.IsReadOnly);
+
+ if (!allPropertiesAreReadOnly)
+ {
+ source.Append(" protected override bool SetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("in godot_variant value)\n {\n");
+
+ isFirstEntry = true;
+ foreach (var property in godotClassProperties)
+ {
+ if (property.PropertySymbol.IsReadOnly)
+ continue;
+
+ GeneratePropertySetter(property.PropertySymbol.Name,
+ property.PropertySymbol.Type, property.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ if (field.FieldSymbol.IsReadOnly)
+ continue;
+
+ GeneratePropertySetter(field.FieldSymbol.Name,
+ field.FieldSymbol.Type, field.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.SetGodotClassPropertyValue(name, value);\n");
+
+ source.Append(" }\n");
+ }
+
+ // Generate GetGodotClassPropertyValue
+
+ source.Append(" protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("out godot_variant value)\n {\n");
+
+ isFirstEntry = true;
+ foreach (var property in godotClassProperties)
+ {
+ GeneratePropertyGetter(property.PropertySymbol.Name,
+ property.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ GeneratePropertyGetter(field.FieldSymbol.Name,
+ field.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.GetGodotClassPropertyValue(name, out value);\n");
+
+ source.Append(" }\n");
+
+ // Generate GetGodotPropertyList
+
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ string dictionaryType = "System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";
+
+ source.Append(" internal new static ")
+ .Append(dictionaryType)
+ .Append(" GetGodotPropertyList()\n {\n");
+
+ source.Append(" var properties = new ")
+ .Append(dictionaryType)
+ .Append("();\n");
+
+ foreach (var property in godotClassProperties)
+ {
+ foreach (var groupingInfo in DetermineGroupingPropertyInfo(property.PropertySymbol))
+ AppendGroupingPropertyInfo(source, groupingInfo);
+
+ var propertyInfo = DeterminePropertyInfo(context, typeCache,
+ property.PropertySymbol, property.Type);
+
+ if (propertyInfo == null)
+ continue;
+
+ AppendPropertyInfo(source, propertyInfo.Value);
+ }
+
+ foreach (var field in godotClassFields)
+ {
+
+ foreach (var groupingInfo in DetermineGroupingPropertyInfo(field.FieldSymbol))
+ AppendGroupingPropertyInfo(source, groupingInfo);
+
+ var propertyInfo = DeterminePropertyInfo(context, typeCache,
+ field.FieldSymbol, field.Type);
+
+ if (propertyInfo == null)
+ continue;
+
+ AppendPropertyInfo(source, propertyInfo.Value);
+ }
+
+ source.Append(" return properties;\n");
+ source.Append(" }\n");
+
+ source.Append("#pragma warning restore CS0109\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void GeneratePropertySetter(
+ string propertyMemberName,
+ ITypeSymbol propertyTypeSymbol,
+ MarshalType propertyMarshalType,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+
+ if (!isFirstEntry)
+ source.Append("else ");
+
+ source.Append("if (name == GodotInternal.PropName_")
+ .Append(propertyMemberName)
+ .Append(") {\n")
+ .Append(" ")
+ .Append(propertyMemberName)
+ .Append(" = ")
+ .AppendNativeVariantToManagedExpr("value", propertyTypeSymbol, propertyMarshalType)
+ .Append(";\n")
+ .Append(" return true;\n")
+ .Append(" }\n");
+ }
+
+ private static void GeneratePropertyGetter(
+ string propertyMemberName,
+ MarshalType propertyMarshalType,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+
+ if (!isFirstEntry)
+ source.Append("else ");
+
+ source.Append("if (name == GodotInternal.PropName_")
+ .Append(propertyMemberName)
+ .Append(") {\n")
+ .Append(" value = ")
+ .AppendManagedToNativeVariantExpr(propertyMemberName, propertyMarshalType)
+ .Append(";\n")
+ .Append(" return true;\n")
+ .Append(" }\n");
+ }
+
+ private static void AppendGroupingPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append(" properties.Add(new(type: (Godot.Variant.Type)")
+ .Append((int)VariantType.Nil)
+ .Append(", name: \"")
+ .Append(propertyInfo.Name)
+ .Append("\", hint: (Godot.PropertyHint)")
+ .Append((int)PropertyHint.None)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: true));\n");
+ }
+
+ private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append(" properties.Add(new(type: (Godot.Variant.Type)")
+ .Append((int)propertyInfo.Type)
+ .Append(", name: GodotInternal.PropName_")
+ .Append(propertyInfo.Name)
+ .Append(", hint: (Godot.PropertyHint)")
+ .Append((int)propertyInfo.Hint)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: ")
+ .Append(propertyInfo.Exported ? "true" : "false")
+ .Append("));\n");
+ }
+
+ private static IEnumerable<PropertyInfo> DetermineGroupingPropertyInfo(ISymbol memberSymbol)
+ {
+ foreach (var attr in memberSymbol.GetAttributes())
+ {
+ PropertyUsageFlags? propertyUsage = attr.AttributeClass?.ToString() switch
+ {
+ GodotClasses.ExportCategoryAttr => PropertyUsageFlags.Category,
+ GodotClasses.ExportGroupAttr => PropertyUsageFlags.Group,
+ GodotClasses.ExportSubgroupAttr => PropertyUsageFlags.Subgroup,
+ _ => null
+ };
+
+ if (propertyUsage is null)
+ continue;
+
+ if (attr.ConstructorArguments.Length > 0 && attr.ConstructorArguments[0].Value is string name)
+ {
+ string? hintString = null;
+ if (propertyUsage != PropertyUsageFlags.Category && attr.ConstructorArguments.Length > 1)
+ hintString = attr.ConstructorArguments[1].Value?.ToString();
+
+ yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, propertyUsage.Value, true);
+ }
+ }
+ }
+
+ private static PropertyInfo? DeterminePropertyInfo(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ ISymbol memberSymbol,
+ MarshalType marshalType
+ )
+ {
+ var exportAttr = memberSymbol.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.IsGodotExportAttribute() ?? false);
+
+ var propertySymbol = memberSymbol as IPropertySymbol;
+ var fieldSymbol = memberSymbol as IFieldSymbol;
+
+ if (exportAttr != null && propertySymbol != null)
+ {
+ if (propertySymbol.GetMethod == null)
+ {
+ // This should never happen, as we filtered WriteOnly properties, but just in case.
+ Common.ReportExportedMemberIsWriteOnly(context, propertySymbol);
+ return null;
+ }
+
+ if (propertySymbol.SetMethod == null)
+ {
+ // This should never happen, as we filtered ReadOnly properties, but just in case.
+ Common.ReportExportedMemberIsReadOnly(context, propertySymbol);
+ return null;
+ }
+ }
+
+ var memberType = propertySymbol?.Type ?? fieldSymbol!.Type;
+
+ var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
+ string memberName = memberSymbol.Name;
+
+ if (exportAttr == null)
+ {
+ return new PropertyInfo(memberVariantType, memberName, PropertyHint.None,
+ hintString: null, PropertyUsageFlags.ScriptVariable, exported: false);
+ }
+
+ if (!TryGetMemberExportHint(typeCache, memberType, exportAttr, memberVariantType,
+ isTypeArgument: false, out var hint, out var hintString))
+ {
+ var constructorArguments = exportAttr.ConstructorArguments;
+
+ if (constructorArguments.Length > 0)
+ {
+ var hintValue = exportAttr.ConstructorArguments[0].Value;
+
+ hint = hintValue switch
+ {
+ null => PropertyHint.None,
+ int intValue => (PropertyHint)intValue,
+ _ => (PropertyHint)(long)hintValue
+ };
+
+ hintString = constructorArguments.Length > 1 ?
+ exportAttr.ConstructorArguments[1].Value?.ToString() :
+ null;
+ }
+ else
+ {
+ hint = PropertyHint.None;
+ }
+ }
+
+ var propUsage = PropertyUsageFlags.Default | PropertyUsageFlags.ScriptVariable;
+
+ if (memberVariantType == VariantType.Nil)
+ propUsage |= PropertyUsageFlags.NilIsVariant;
+
+ return new PropertyInfo(memberVariantType, memberName,
+ hint, hintString, propUsage, exported: true);
+ }
+
+ private static bool TryGetMemberExportHint(
+ MarshalUtils.TypeCache typeCache,
+ ITypeSymbol type, AttributeData exportAttr,
+ VariantType variantType, bool isTypeArgument,
+ out PropertyHint hint, out string? hintString
+ )
+ {
+ hint = PropertyHint.None;
+ hintString = null;
+
+ if (variantType == VariantType.Nil)
+ return true; // Variant, no export hint
+
+ if (variantType == VariantType.Int &&
+ type.IsValueType && type.TypeKind == TypeKind.Enum)
+ {
+ bool hasFlagsAttr = type.GetAttributes()
+ .Any(a => a.AttributeClass?.IsSystemFlagsAttribute() ?? false);
+
+ hint = hasFlagsAttr ? PropertyHint.Flags : PropertyHint.Enum;
+
+ var members = type.GetMembers();
+
+ var enumFields = members
+ .Where(s => s.Kind == SymbolKind.Field && s.IsStatic &&
+ s.DeclaredAccessibility == Accessibility.Public &&
+ !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>().ToArray();
+
+ var hintStringBuilder = new StringBuilder();
+ var nameOnlyHintStringBuilder = new StringBuilder();
+
+ // True: enum Foo { Bar, Baz, Qux }
+ // True: enum Foo { Bar = 0, Baz = 1, Qux = 2 }
+ // False: enum Foo { Bar = 0, Baz = 7, Qux = 5 }
+ bool usesDefaultValues = true;
+
+ for (int i = 0; i < enumFields.Length; i++)
+ {
+ var enumField = enumFields[i];
+
+ if (i > 0)
+ {
+ hintStringBuilder.Append(",");
+ nameOnlyHintStringBuilder.Append(",");
+ }
+
+ string enumFieldName = enumField.Name;
+ hintStringBuilder.Append(enumFieldName);
+ nameOnlyHintStringBuilder.Append(enumFieldName);
+
+ long val = enumField.ConstantValue switch
+ {
+ sbyte v => v,
+ short v => v,
+ int v => v,
+ long v => v,
+ byte v => v,
+ ushort v => v,
+ uint v => v,
+ ulong v => (long)v,
+ _ => 0
+ };
+
+ uint expectedVal = (uint)(hint == PropertyHint.Flags ? 1 << i : i);
+ if (val != expectedVal)
+ usesDefaultValues = false;
+
+ hintStringBuilder.Append(":");
+ hintStringBuilder.Append(val);
+ }
+
+ hintString = !usesDefaultValues ?
+ hintStringBuilder.ToString() :
+ // If we use the format NAME:VAL, that's what the editor displays.
+ // That's annoying if the user is not using custom values for the enum constants.
+ // This may not be needed in the future if the editor is changed to not display values.
+ nameOnlyHintStringBuilder.ToString();
+
+ return true;
+ }
+
+ if (variantType == VariantType.Object && type is INamedTypeSymbol memberNamedType)
+ {
+ if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Resource"))
+ {
+ string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;
+
+ hint = PropertyHint.ResourceType;
+ hintString = nativeTypeName;
+
+ return true;
+ }
+
+ if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Node"))
+ {
+ string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;
+
+ hint = PropertyHint.NodeType;
+ hintString = nativeTypeName;
+
+ return true;
+ }
+ }
+
+ static bool GetStringArrayEnumHint(VariantType elementVariantType,
+ AttributeData exportAttr, out string? hintString)
+ {
+ var constructorArguments = exportAttr.ConstructorArguments;
+
+ if (constructorArguments.Length > 0)
+ {
+ var presetHintValue = exportAttr.ConstructorArguments[0].Value;
+
+ PropertyHint presetHint = presetHintValue switch
+ {
+ null => PropertyHint.None,
+ int intValue => (PropertyHint)intValue,
+ _ => (PropertyHint)(long)presetHintValue
+ };
+
+ if (presetHint == PropertyHint.Enum)
+ {
+ string? presetHintString = constructorArguments.Length > 1 ?
+ exportAttr.ConstructorArguments[1].Value?.ToString() :
+ null;
+
+ hintString = (int)elementVariantType + "/" + (int)PropertyHint.Enum + ":";
+
+ if (presetHintString != null)
+ hintString += presetHintString;
+
+ return true;
+ }
+ }
+
+ hintString = null;
+ return false;
+ }
+
+ if (!isTypeArgument && variantType == VariantType.Array)
+ {
+ var elementType = MarshalUtils.GetArrayElementType(type);
+
+ if (elementType == null)
+ return false; // Non-generic Array, so there's no hint to add
+
+ var elementMarshalType = MarshalUtils.ConvertManagedTypeToMarshalType(elementType, typeCache)!.Value;
+ var elementVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(elementMarshalType)!.Value;
+
+ bool isPresetHint = false;
+
+ if (elementVariantType == VariantType.String)
+ isPresetHint = GetStringArrayEnumHint(elementVariantType, exportAttr, out hintString);
+
+ if (!isPresetHint)
+ {
+ bool hintRes = TryGetMemberExportHint(typeCache, elementType,
+ exportAttr, elementVariantType, isTypeArgument: true,
+ out var elementHint, out var elementHintString);
+
+ // Format: type/hint:hint_string
+ if (hintRes)
+ {
+ hintString = (int)elementVariantType + "/" + (int)elementHint + ":";
+
+ if (elementHintString != null)
+ hintString += elementHintString;
+ }
+ else
+ {
+ hintString = (int)elementVariantType + "/" + (int)PropertyHint.None + ":";
+ }
+ }
+
+ hint = PropertyHint.TypeString;
+
+ return hintString != null;
+ }
+
+ if (!isTypeArgument && variantType == VariantType.PackedStringArray)
+ {
+ if (GetStringArrayEnumHint(VariantType.String, exportAttr, out hintString))
+ {
+ hint = PropertyHint.TypeString;
+ return true;
+ }
+ }
+
+ if (!isTypeArgument && variantType == VariantType.Dictionary)
+ {
+ // TODO: Dictionaries are not supported in the inspector
+ return false;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
new file mode 100644
index 0000000000..85fadaa52e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -0,0 +1,293 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptPropertyDefValGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptPropertyDefVal_Generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var exportedMembers = new List<ExportedPropertyMetadata>();
+
+ var members = symbol.GetMembers();
+
+ var exportedProperties = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property)
+ .Cast<IPropertySymbol>()
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotExportAttribute() ?? false))
+ .ToArray();
+
+ var exportedFields = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>()
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotExportAttribute() ?? false))
+ .ToArray();
+
+ foreach (var property in exportedProperties)
+ {
+ if (property.IsStatic)
+ {
+ Common.ReportExportedMemberIsStatic(context, property);
+ continue;
+ }
+
+ // TODO: We should still restore read-only properties after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
+ if (property.IsWriteOnly)
+ {
+ Common.ReportExportedMemberIsWriteOnly(context, property);
+ continue;
+ }
+
+ if (property.IsReadOnly)
+ {
+ Common.ReportExportedMemberIsReadOnly(context, property);
+ continue;
+ }
+
+
+ var propertyType = property.Type;
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(propertyType, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportExportedMemberTypeNotSupported(context, property);
+ continue;
+ }
+
+ // TODO: Detect default value from simple property getters (currently we only detect from initializers)
+
+ EqualsValueClauseSyntax? initializer = property.DeclaringSyntaxReferences
+ .Select(r => r.GetSyntax() as PropertyDeclarationSyntax)
+ .Select(s => s?.Initializer ?? null)
+ .FirstOrDefault();
+
+ string? value = initializer?.Value.ToString();
+
+ exportedMembers.Add(new ExportedPropertyMetadata(
+ property.Name, marshalType.Value, propertyType, value));
+ }
+
+ foreach (var field in exportedFields)
+ {
+ if (field.IsStatic)
+ {
+ Common.ReportExportedMemberIsStatic(context, field);
+ continue;
+ }
+
+ // TODO: We should still restore read-only fields after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
+ if (field.IsReadOnly)
+ {
+ Common.ReportExportedMemberIsReadOnly(context, field);
+ continue;
+ }
+
+ var fieldType = field.Type;
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(fieldType, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportExportedMemberTypeNotSupported(context, field);
+ continue;
+ }
+
+ EqualsValueClauseSyntax? initializer = field.DeclaringSyntaxReferences
+ .Select(r => r.GetSyntax())
+ .OfType<VariableDeclaratorSyntax>()
+ .Select(s => s.Initializer)
+ .FirstOrDefault(i => i != null);
+
+ string? value = initializer?.Value.ToString();
+
+ exportedMembers.Add(new ExportedPropertyMetadata(
+ field.Name, marshalType.Value, fieldType, value));
+ }
+
+ // Generate GetGodotExportedProperties
+
+ if (exportedMembers.Count > 0)
+ {
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ string dictionaryType = "System.Collections.Generic.Dictionary<StringName, object>";
+
+ source.Append("#if TOOLS\n");
+ source.Append(" internal new static ");
+ source.Append(dictionaryType);
+ source.Append(" GetGodotPropertyDefaultValues()\n {\n");
+
+ source.Append(" var values = new ");
+ source.Append(dictionaryType);
+ source.Append("(");
+ source.Append(exportedMembers.Count);
+ source.Append(");\n");
+
+ foreach (var exportedMember in exportedMembers)
+ {
+ string defaultValueLocalName = string.Concat("__", exportedMember.Name, "_default_value");
+
+ source.Append(" ");
+ source.Append(exportedMember.TypeSymbol.FullQualifiedName());
+ source.Append(" ");
+ source.Append(defaultValueLocalName);
+ source.Append(" = ");
+ source.Append(exportedMember.Value ?? "default");
+ source.Append(";\n");
+ source.Append(" values.Add(GodotInternal.PropName_");
+ source.Append(exportedMember.Name);
+ source.Append(", ");
+ source.Append(defaultValueLocalName);
+ source.Append(");\n");
+ }
+
+ source.Append(" return values;\n");
+ source.Append(" }\n");
+ source.Append("#endif\n");
+
+ source.Append("#pragma warning restore CS0109\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private struct ExportedPropertyMetadata
+ {
+ public ExportedPropertyMetadata(string name, MarshalType type, ITypeSymbol typeSymbol, string? value)
+ {
+ Name = name;
+ Type = type;
+ TypeSymbol = typeSymbol;
+ Value = value;
+ }
+
+ public string Name { get; }
+ public MarshalType Type { get; }
+ public ITypeSymbol TypeSymbol { get; }
+ public string? Value { get; }
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs
new file mode 100644
index 0000000000..ec04a319e2
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs
@@ -0,0 +1,19 @@
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ // Placeholder. Once we switch to native extensions this will act as the registrar for all
+ // user Godot classes in the assembly. Think of it as something similar to `register_types`.
+ public class ScriptRegistrarGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
new file mode 100644
index 0000000000..5a84122c4c
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
@@ -0,0 +1,283 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptSerializationGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptSerialization_Generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ var propertySymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property)
+ .Cast<IPropertySymbol>();
+
+ var fieldSymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>();
+
+ var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+ var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+
+ var signalDelegateSymbols = members
+ .Where(s => s.Kind == SymbolKind.NamedType)
+ .Cast<INamedTypeSymbol>()
+ .Where(namedTypeSymbol => namedTypeSymbol.TypeKind == TypeKind.Delegate)
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false));
+
+ List<GodotSignalDelegateData> godotSignalDelegates = new();
+
+ foreach (var signalDelegateSymbol in signalDelegateSymbols)
+ {
+ if (!signalDelegateSymbol.Name.EndsWith(ScriptSignalsGenerator.SignalDelegateSuffix))
+ continue;
+
+ string signalName = signalDelegateSymbol.Name;
+ signalName = signalName.Substring(0,
+ signalName.Length - ScriptSignalsGenerator.SignalDelegateSuffix.Length);
+
+ var invokeMethodData = signalDelegateSymbol
+ .DelegateInvokeMethod?.HasGodotCompatibleSignature(typeCache);
+
+ if (invokeMethodData == null)
+ continue;
+
+ godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
+ }
+
+ source.Append(
+ " protected override void SaveGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n {\n");
+ source.Append(" base.SaveGodotObjectData(info);\n");
+
+ // Save properties
+
+ foreach (var property in godotClassProperties)
+ {
+ string propertyName = property.PropertySymbol.Name;
+
+ source.Append(" info.AddProperty(GodotInternal.PropName_")
+ .Append(propertyName)
+ .Append(", ")
+ .AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type)
+ .Append(");\n");
+ }
+
+ // Save fields
+
+ foreach (var field in godotClassFields)
+ {
+ string fieldName = field.FieldSymbol.Name;
+
+ source.Append(" info.AddProperty(GodotInternal.PropName_")
+ .Append(fieldName)
+ .Append(", ")
+ .AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type)
+ .Append(");\n");
+ }
+
+ // Save signal events
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+
+ source.Append(" info.AddSignalEventDelegate(GodotInternal.SignalName_")
+ .Append(signalName)
+ .Append(", this.backing_")
+ .Append(signalName)
+ .Append(");\n");
+ }
+
+ source.Append(" }\n");
+
+ source.Append(
+ " protected override void RestoreGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n {\n");
+ source.Append(" base.RestoreGodotObjectData(info);\n");
+
+ // Restore properties
+
+ foreach (var property in godotClassProperties)
+ {
+ string propertyName = property.PropertySymbol.Name;
+
+ source.Append(" if (info.TryGetProperty(GodotInternal.PropName_")
+ .Append(propertyName)
+ .Append(", out var _value_")
+ .Append(propertyName)
+ .Append("))\n")
+ .Append(" this.")
+ .Append(propertyName)
+ .Append(" = ")
+ .AppendVariantToManagedExpr(string.Concat("_value_", propertyName),
+ property.PropertySymbol.Type, property.Type)
+ .Append(";\n");
+ }
+
+ // Restore fields
+
+ foreach (var field in godotClassFields)
+ {
+ string fieldName = field.FieldSymbol.Name;
+
+ source.Append(" if (info.TryGetProperty(GodotInternal.PropName_")
+ .Append(fieldName)
+ .Append(", out var _value_")
+ .Append(fieldName)
+ .Append("))\n")
+ .Append(" this.")
+ .Append(fieldName)
+ .Append(" = ")
+ .AppendVariantToManagedExpr(string.Concat("_value_", fieldName),
+ field.FieldSymbol.Type, field.Type)
+ .Append(";\n");
+ }
+
+ // Restore signal events
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+ string signalDelegateQualifiedName = signalDelegate.DelegateSymbol.FullQualifiedName();
+
+ source.Append(" if (info.TryGetSignalEventDelegate<")
+ .Append(signalDelegateQualifiedName)
+ .Append(">(GodotInternal.SignalName_")
+ .Append(signalName)
+ .Append(", out var _value_")
+ .Append(signalName)
+ .Append("))\n")
+ .Append(" this.backing_")
+ .Append(signalName)
+ .Append(" = _value_")
+ .Append(signalName)
+ .Append(";\n");
+ }
+
+ source.Append(" }\n");
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
new file mode 100644
index 0000000000..161834a4be
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -0,0 +1,432 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+// TODO:
+// Determine a proper way to emit the signal.
+// 'Emit(nameof(TheEvent))' creates a StringName everytime and has the overhead of string marshaling.
+// I haven't decided on the best option yet. Some possibilities:
+// - Expose the generated StringName fields to the user, for use with 'Emit(...)'.
+// - Generate a 'EmitSignalName' method for each event signal.
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptSignalsGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ internal static string SignalDelegateSuffix = "EventHandler";
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptSignals_Generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ // TODO:
+ // The delegate name already needs to end with 'Signal' to avoid collision with the event name.
+ // Requiring SignalAttribute is redundant. Should we remove it to make declaration shorter?
+
+ var members = symbol.GetMembers();
+
+ var signalDelegateSymbols = members
+ .Where(s => s.Kind == SymbolKind.NamedType)
+ .Cast<INamedTypeSymbol>()
+ .Where(namedTypeSymbol => namedTypeSymbol.TypeKind == TypeKind.Delegate)
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false));
+
+ List<GodotSignalDelegateData> godotSignalDelegates = new();
+
+ foreach (var signalDelegateSymbol in signalDelegateSymbols)
+ {
+ if (!signalDelegateSymbol.Name.EndsWith(SignalDelegateSuffix))
+ {
+ Common.ReportSignalDelegateMissingSuffix(context, signalDelegateSymbol);
+ continue;
+ }
+
+ string signalName = signalDelegateSymbol.Name;
+ signalName = signalName.Substring(0, signalName.Length - SignalDelegateSuffix.Length);
+
+ var invokeMethodData = signalDelegateSymbol
+ .DelegateInvokeMethod?.HasGodotCompatibleSignature(typeCache);
+
+ if (invokeMethodData == null)
+ {
+ if (signalDelegateSymbol.DelegateInvokeMethod is IMethodSymbol methodSymbol)
+ {
+ foreach (var parameter in methodSymbol.Parameters)
+ {
+ if (parameter.RefKind != RefKind.None)
+ {
+ Common.ReportSignalParameterTypeNotSupported(context, parameter);
+ continue;
+ }
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(parameter.Type, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportSignalParameterTypeNotSupported(context, parameter);
+ }
+ }
+
+ if (!methodSymbol.ReturnsVoid)
+ {
+ Common.ReportSignalDelegateSignatureMustReturnVoid(context, signalDelegateSymbol);
+ }
+ }
+ continue;
+ }
+
+ godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
+ }
+
+ source.Append(" private partial class GodotInternal {\n");
+
+ // Generate cached StringNames for methods and properties, for fast lookup
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+ source.Append(" public static readonly StringName SignalName_");
+ source.Append(signalName);
+ source.Append(" = \"");
+ source.Append(signalName);
+ source.Append("\";\n");
+ }
+
+ source.Append(" }\n"); // class GodotInternal
+
+ // Generate GetGodotSignalList
+
+ if (godotSignalDelegates.Count > 0)
+ {
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ const string listType = "System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
+
+ source.Append(" internal new static ")
+ .Append(listType)
+ .Append(" GetGodotSignalList()\n {\n");
+
+ source.Append(" var signals = new ")
+ .Append(listType)
+ .Append("(")
+ .Append(godotSignalDelegates.Count)
+ .Append(");\n");
+
+ foreach (var signalDelegateData in godotSignalDelegates)
+ {
+ var methodInfo = DetermineMethodInfo(signalDelegateData);
+ AppendMethodInfo(source, methodInfo);
+ }
+
+ source.Append(" return signals;\n");
+ source.Append(" }\n");
+
+ source.Append("#pragma warning restore CS0109\n");
+ }
+
+ // Generate signal event
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+
+ // TODO: Hide backing event from code-completion and debugger
+ // The reason we have a backing field is to hide the invoke method from the event,
+ // as it doesn't emit the signal, only the event delegates. This can confuse users.
+ // Maybe we should directly connect the delegates, as we do with native signals?
+ source.Append(" private ")
+ .Append(signalDelegate.DelegateSymbol.FullQualifiedName())
+ .Append(" backing_")
+ .Append(signalName)
+ .Append(";\n");
+
+ source.Append(" public event ")
+ .Append(signalDelegate.DelegateSymbol.FullQualifiedName())
+ .Append(" ")
+ .Append(signalName)
+ .Append(" {\n")
+ .Append(" add => backing_")
+ .Append(signalName)
+ .Append(" += value;\n")
+ .Append(" remove => backing_")
+ .Append(signalName)
+ .Append(" -= value;\n")
+ .Append("}\n");
+ }
+
+ // Generate RaiseGodotClassSignalCallbacks
+
+ if (godotSignalDelegates.Count > 0)
+ {
+ source.Append(
+ " protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, ");
+ source.Append("NativeVariantPtrArgs args, int argCount)\n {\n");
+
+ foreach (var signal in godotSignalDelegates)
+ {
+ GenerateSignalEventInvoker(signal, source);
+ }
+
+ source.Append(" base.RaiseGodotClassSignalCallbacks(signal, args, argCount);\n");
+
+ source.Append(" }\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
+ {
+ source.Append(" signals.Add(new(name: GodotInternal.SignalName_")
+ .Append(methodInfo.Name)
+ .Append(", returnVal: ");
+
+ AppendPropertyInfo(source, methodInfo.ReturnVal);
+
+ source.Append(", flags: (Godot.MethodFlags)")
+ .Append((int)methodInfo.Flags)
+ .Append(", arguments: ");
+
+ if (methodInfo.Arguments is { Count: > 0 })
+ {
+ source.Append("new() { ");
+
+ foreach (var param in methodInfo.Arguments)
+ {
+ AppendPropertyInfo(source, param);
+
+ // C# allows colon after the last element
+ source.Append(", ");
+ }
+
+ source.Append(" }");
+ }
+ else
+ {
+ source.Append("null");
+ }
+
+ source.Append(", defaultArguments: null));\n");
+ }
+
+ private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append("new(type: (Godot.Variant.Type)")
+ .Append((int)propertyInfo.Type)
+ .Append(", name: \"")
+ .Append(propertyInfo.Name)
+ .Append("\", hint: (Godot.PropertyHint)")
+ .Append((int)propertyInfo.Hint)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: ")
+ .Append(propertyInfo.Exported ? "true" : "false")
+ .Append(")");
+ }
+
+ private static MethodInfo DetermineMethodInfo(GodotSignalDelegateData signalDelegateData)
+ {
+ var invokeMethodData = signalDelegateData.InvokeMethodData;
+
+ PropertyInfo returnVal;
+
+ if (invokeMethodData.RetType != null)
+ {
+ returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value, name: string.Empty);
+ }
+ else
+ {
+ returnVal = new PropertyInfo(VariantType.Nil, string.Empty, PropertyHint.None,
+ hintString: null, PropertyUsageFlags.Default, exported: false);
+ }
+
+ int paramCount = invokeMethodData.ParamTypes.Length;
+
+ List<PropertyInfo>? arguments;
+
+ if (paramCount > 0)
+ {
+ arguments = new(capacity: paramCount);
+
+ for (int i = 0; i < paramCount; i++)
+ {
+ arguments.Add(DeterminePropertyInfo(invokeMethodData.ParamTypes[i],
+ name: invokeMethodData.Method.Parameters[i].Name));
+ }
+ }
+ else
+ {
+ arguments = null;
+ }
+
+ return new MethodInfo(signalDelegateData.Name, returnVal, MethodFlags.Default, arguments,
+ defaultArguments: null);
+ }
+
+ private static PropertyInfo DeterminePropertyInfo(MarshalType marshalType, string name)
+ {
+ var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
+
+ var propUsage = PropertyUsageFlags.Default;
+
+ if (memberVariantType == VariantType.Nil)
+ propUsage |= PropertyUsageFlags.NilIsVariant;
+
+ return new PropertyInfo(memberVariantType, name,
+ PropertyHint.None, string.Empty, propUsage, exported: false);
+ }
+
+ private static void GenerateSignalEventInvoker(
+ GodotSignalDelegateData signal,
+ StringBuilder source
+ )
+ {
+ string signalName = signal.Name;
+ var invokeMethodData = signal.InvokeMethodData;
+
+ source.Append(" if (signal == GodotInternal.SignalName_");
+ source.Append(signalName);
+ source.Append(" && argCount == ");
+ source.Append(invokeMethodData.ParamTypes.Length);
+ source.Append(") {\n");
+ source.Append(" backing_");
+ source.Append(signalName);
+ source.Append("?.Invoke(");
+
+ for (int i = 0; i < invokeMethodData.ParamTypes.Length; i++)
+ {
+ if (i != 0)
+ source.Append(", ");
+
+ source.AppendNativeVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
+ invokeMethodData.ParamTypeSymbols[i], invokeMethodData.ParamTypes[i]);
+ }
+
+ source.Append(");\n");
+
+ source.Append(" return;\n");
+
+ source.Append(" }\n");
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index 2bf1cb7a18..01aa65bfc3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -7,8 +7,6 @@ namespace GodotTools.BuildLogger
{
public class GodotBuildLogger : ILogger
{
- public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location);
-
public string Parameters { get; set; }
public LoggerVerbosity Verbosity { get; set; }
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
index 0afec970c6..9e36497b06 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
@@ -5,6 +5,6 @@
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Build.Framework" Version="16.5.0" />
+ <PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
index d6d8962f90..cfd5c88a58 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
- <TargetFramework>netstandard2.0</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
</PropertyGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
index 303ca3a293..d2132115f3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{B06C2951-C8E3-4F28-80B2-717CF327EB19}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
index 5b3ed0b1b7..c05096bdcc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index 37123ba2b2..bde14b2b40 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -1,32 +1,16 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
- <TargetFramework>net472</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Build" Version="16.5.0" />
+ <PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" />
+ <PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
<ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
</ItemGroup>
- <!--
- The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
- here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
- We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
- searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
- -->
- <ItemGroup>
- <None Include="MSBuild.exe" CopyToOutputDirectory="Always" />
- </ItemGroup>
- <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) ">
- <PropertyGroup>
- <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
- <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
- </PropertyGroup>
- <!-- Need to copy it here as well on Windows -->
- <Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" />
- </Target>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe
deleted file mode 100644
index e69de29bb2..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe
+++ /dev/null
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 7d49d251dd..fb6d2a707b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.Build.Construction;
@@ -21,7 +22,8 @@ namespace GodotTools.ProjectEditor
root.Sdk = GodotSdkAttrValue;
var mainGroup = root.AddPropertyGroup();
- mainGroup.AddProperty("TargetFramework", "netstandard2.1");
+ mainGroup.AddProperty("TargetFramework", "net6.0");
+ mainGroup.AddProperty("EnableDynamicLoading", "true");
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
@@ -44,7 +46,7 @@ namespace GodotTools.ProjectEditor
// Save (without BOM)
root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
- return Guid.NewGuid().ToString().ToUpper();
+ return Guid.NewGuid().ToString().ToUpperInvariant();
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index cdac9acb25..7b1d5c228a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -19,6 +19,16 @@ namespace GodotTools.ProjectEditor
public static class ProjectUtils
{
+ public static void MSBuildLocatorRegisterDefaults(out Version version, out string path)
+ {
+ var instance = Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
+ version = instance.Version;
+ path = instance.MSBuildPath;
+ }
+
+ public static void MSBuildLocatorRegisterMSBuildPath(string msbuildPath)
+ => Microsoft.Build.Locator.MSBuildLocator.RegisterMSBuildPath(msbuildPath);
+
public static MSBuildProject Open(string path)
{
var root = ProjectRootElement.Open(path);
@@ -42,7 +52,8 @@ namespace GodotTools.ProjectEditor
var root = project.Root;
string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
- if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(root.Sdk) &&
+ root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
return;
root.Sdk = godotSdkAttrValue;
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
index aab2d73bdd..4baae77b34 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -8,8 +8,8 @@
</Target>
<Target Name="GenerateGodotNupkgsVersionsFile"
- DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile"
- BeforeTargets="BeforeCompile;CoreCompile">
+ DependsOnTargets="_GenerateGodotNupkgsVersionsFile"
+ BeforeTargets="PrepareForBuild;CompileDesignTime;BeforeCompile;CoreCompile">
<ItemGroup>
<Compile Include="$(GeneratedGodotNupkgsVersionsFile)" />
<FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" />
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
index 3bc1698c15..d60e6343ea 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
@@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netstandard2.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
+ <!-- Specify compile items manually to avoid including dangling generated items. -->
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<Import Project="GenerateGodotNupkgsVersions.targets" />
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index d3107a69db..564775635d 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.ProjectEditor", "GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
@@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj", "{D8C421B2-8911-41EB-B983-F675C7141EB7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{55666071-BEC1-4A52-8A98-9A4A7A947DBF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -49,5 +53,13 @@ Global
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index 28bf57dc21..3c5b897719 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -4,26 +4,36 @@ using Godot.Collections;
using GodotTools.Internals;
using Path = System.IO.Path;
+#nullable enable
+
namespace GodotTools.Build
{
[Serializable]
- public sealed class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
+ public sealed partial class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
{
- public string Solution { get; }
- public string[] Targets { get; }
- public string Configuration { get; }
- public bool Restore { get; }
+ public string Solution { get; private set; }
+ public string Configuration { get; private set; }
+ public string? RuntimeIdentifier { get; private set; }
+ public string? PublishOutputDir { get; private set; }
+ public bool Restore { get; private set; }
+ public bool Rebuild { get; private set; }
+ public bool OnlyClean { get; private set; }
+
// TODO Use List once we have proper serialization
- public Array<string> CustomProperties { get; } = new Array<string>();
+ public Godot.Collections.Array CustomProperties { get; private set; } = new();
- public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
+ public string LogsDirPath =>
+ Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
if (obj is BuildInfo other)
- return other.Solution == Solution && other.Targets == Targets &&
- other.Configuration == Configuration && other.Restore == Restore &&
- other.CustomProperties == CustomProperties && other.LogsDirPath == LogsDirPath;
+ return other.Solution == Solution &&
+ other.Configuration == Configuration && other.RuntimeIdentifier == RuntimeIdentifier &&
+ other.PublishOutputDir == PublishOutputDir && other.Restore == Restore &&
+ other.Rebuild == Rebuild && other.OnlyClean == OnlyClean &&
+ other.CustomProperties == CustomProperties &&
+ other.LogsDirPath == LogsDirPath;
return false;
}
@@ -34,25 +44,44 @@ namespace GodotTools.Build
{
int hash = 17;
hash = (hash * 29) + Solution.GetHashCode();
- hash = (hash * 29) + Targets.GetHashCode();
hash = (hash * 29) + Configuration.GetHashCode();
+ hash = (hash * 29) + (RuntimeIdentifier?.GetHashCode() ?? 0);
+ hash = (hash * 29) + (PublishOutputDir?.GetHashCode() ?? 0);
hash = (hash * 29) + Restore.GetHashCode();
+ hash = (hash * 29) + Rebuild.GetHashCode();
+ hash = (hash * 29) + OnlyClean.GetHashCode();
hash = (hash * 29) + CustomProperties.GetHashCode();
hash = (hash * 29) + LogsDirPath.GetHashCode();
return hash;
}
}
+ // Needed for instantiation from Godot, after reloading assemblies
private BuildInfo()
{
+ Solution = string.Empty;
+ Configuration = string.Empty;
+ }
+
+ public BuildInfo(string solution, string configuration, bool restore, bool rebuild, bool onlyClean)
+ {
+ Solution = solution;
+ Configuration = configuration;
+ Restore = restore;
+ Rebuild = rebuild;
+ OnlyClean = onlyClean;
}
- public BuildInfo(string solution, string[] targets, string configuration, bool restore)
+ public BuildInfo(string solution, string configuration, string runtimeIdentifier,
+ string publishOutputDir, bool restore, bool rebuild, bool onlyClean)
{
Solution = solution;
- Targets = targets;
Configuration = configuration;
+ RuntimeIdentifier = runtimeIdentifier;
+ PublishOutputDir = publishOutputDir;
Restore = restore;
+ Rebuild = rebuild;
+ OnlyClean = onlyClean;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 21bff70b15..43256953f5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -1,12 +1,10 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
-using GodotTools.Ides.Rider;
+using Godot;
using GodotTools.Internals;
-using JetBrains.Annotations;
-using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
-using OS = GodotTools.Utils.OS;
namespace GodotTools.Build
{
@@ -14,13 +12,8 @@ namespace GodotTools.Build
{
private static BuildInfo _buildInProgress;
- public const string PropNameMSBuildMono = "MSBuild (Mono)";
- public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)";
- public const string PropNameMSBuildJetBrains = "MSBuild (JetBrains Rider)";
- public const string PropNameDotnetCli = "dotnet CLI";
-
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
- public const string MsBuildLogFileName = "msbuild_log.txt";
+ private const string MsBuildLogFileName = "msbuild_log.txt";
public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason);
@@ -62,11 +55,11 @@ namespace GodotTools.Build
private static void PrintVerbose(string text)
{
- if (Godot.OS.IsStdoutVerbose())
- Godot.GD.Print(text);
+ if (OS.IsStdoutVerbose())
+ GD.Print(text);
}
- public static bool Build(BuildInfo buildInfo)
+ private static bool Build(BuildInfo buildInfo)
{
if (_buildInProgress != null)
throw new InvalidOperationException("A build is already in progress");
@@ -103,7 +96,8 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
@@ -148,7 +142,8 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
@@ -159,18 +154,54 @@ namespace GodotTools.Build
}
}
- public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null)
+ private static bool Publish(BuildInfo buildInfo)
{
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true);
+ if (_buildInProgress != null)
+ throw new InvalidOperationException("A build is already in progress");
- // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
- if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
- buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+ _buildInProgress = buildInfo;
- if (Internal.GodotIsRealTDouble())
- buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+ try
+ {
+ BuildStarted?.Invoke(buildInfo);
+
+ // Required in order to update the build tasks list
+ Internal.GodotMainIteration();
+
+ try
+ {
+ RemoveOldIssuesFile(buildInfo);
+ }
+ catch (IOException e)
+ {
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ Console.Error.WriteLine(e);
+ }
+
+ try
+ {
+ int exitCode = BuildSystem.Publish(buildInfo, StdOutputReceived, StdErrorReceived);
+
+ if (exitCode != 0)
+ PrintVerbose(
+ $"dotnet publish exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
- return BuildProjectBlocking(buildInfo);
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
+
+ return exitCode == 0;
+ }
+ catch (Exception e)
+ {
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The publish method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ Console.Error.WriteLine(e);
+ return false;
+ }
+ }
+ finally
+ {
+ _buildInProgress = null;
+ }
}
private static bool BuildProjectBlocking(BuildInfo buildInfo)
@@ -178,31 +209,109 @@ namespace GodotTools.Build
if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
- // Make sure the API assemblies are up to date before building the project.
- // We may not have had the chance to update the release API assemblies, and the debug ones
- // may have been deleted by the user at some point after they were loaded by the Godot editor.
- string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
+ using var pr = new EditorProgress("dotnet_build_project", "Building .NET project...", 1);
+
+ pr.Step("Building project solution", 0);
- if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
+ if (!Build(buildInfo))
{
- ShowBuildErrorDialog("Failed to update the Godot API assemblies");
+ ShowBuildErrorDialog("Failed to build project solution");
return false;
}
- using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
+ return true;
+ }
+
+ private static bool CleanProjectBlocking(BuildInfo buildInfo)
+ {
+ if (!File.Exists(buildInfo.Solution))
+ return true; // No solution to clean
+
+ using var pr = new EditorProgress("dotnet_clean_project", "Cleaning .NET project...", 1);
+
+ pr.Step("Cleaning project solution", 0);
+
+ if (!Build(buildInfo))
{
- pr.Step("Building project solution", 0);
+ ShowBuildErrorDialog("Failed to clean project solution");
+ return false;
+ }
- if (!Build(buildInfo))
- {
- ShowBuildErrorDialog("Failed to build project solution");
- return false;
- }
+ return true;
+ }
+
+ private static bool PublishProjectBlocking(BuildInfo buildInfo)
+ {
+ using var pr = new EditorProgress("dotnet_publish_project", "Publishing .NET project...", 1);
+
+ pr.Step("Running dotnet publish", 0);
+
+ if (!Publish(buildInfo))
+ {
+ ShowBuildErrorDialog("Failed to publish .NET project");
+ return false;
}
return true;
}
+ private static BuildInfo CreateBuildInfo(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null,
+ bool rebuild = false,
+ bool onlyClean = false
+ )
+ {
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ restore: true, rebuild, onlyClean);
+
+ // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
+ if (platform != null || Utils.OS.PlatformNameMap.TryGetValue(OS.GetName(), out platform))
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return buildInfo;
+ }
+
+ private static BuildInfo CreatePublishBuildInfo(
+ [DisallowNull] string configuration,
+ [DisallowNull] string platform,
+ [DisallowNull] string runtimeIdentifier,
+ [DisallowNull] string publishOutputDir
+ )
+ {
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ runtimeIdentifier, publishOutputDir, restore: true, rebuild: false, onlyClean: false);
+
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return buildInfo;
+ }
+
+ public static bool BuildProjectBlocking(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null,
+ bool rebuild = false
+ ) => BuildProjectBlocking(CreateBuildInfo(configuration, platform, rebuild));
+
+ public static bool CleanProjectBlocking(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null
+ ) => CleanProjectBlocking(CreateBuildInfo(configuration, platform, rebuild: false));
+
+ public static bool PublishProjectBlocking(
+ [DisallowNull] string configuration,
+ [DisallowNull] string platform,
+ [DisallowNull] string runtimeIdentifier,
+ string publishOutputDir
+ ) => PublishProjectBlocking(CreatePublishBuildInfo(configuration,
+ platform, runtimeIdentifier, publishOutputDir));
+
public static bool EditorBuildCallback()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -215,7 +324,7 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
}
if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
@@ -226,47 +335,6 @@ namespace GodotTools.Build
public static void Initialize()
{
- // Build tool settings
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
-
- BuildTool msbuildDefault;
-
- if (OS.IsWindows)
- {
- if (RiderPathManager.IsExternalEditorSetToRider(editorSettings))
- msbuildDefault = BuildTool.JetBrainsMsBuild;
- else
- msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildVs;
- }
- else
- {
- msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildMono;
- }
-
- EditorDef("mono/builds/build_tool", msbuildDefault);
-
- string hintString;
-
- if (OS.IsWindows)
- {
- hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
- $"{PropNameMSBuildVs}:{(int)BuildTool.MsBuildVs}," +
- $"{PropNameMSBuildJetBrains}:{(int)BuildTool.JetBrainsMsBuild}," +
- $"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
- }
- else
- {
- hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
- $"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
- }
-
- editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
- {
- ["type"] = Godot.Variant.Type.Int,
- ["name"] = "mono/builds/build_tool",
- ["hint"] = Godot.PropertyHint.Enum,
- ["hint_string"] = hintString
- });
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index ebdaca0ce8..96d1fc28bf 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -1,17 +1,16 @@
using Godot;
using System;
-using Godot.Collections;
+using System.Diagnostics.CodeAnalysis;
using GodotTools.Internals;
-using JetBrains.Annotations;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
namespace GodotTools.Build
{
- public class BuildOutputView : VBoxContainer, ISerializationListener
+ public partial class BuildOutputView : VBoxContainer, ISerializationListener
{
[Serializable]
- private class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
+ private partial class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
{
public bool Warning { get; set; }
public string File { get; set; }
@@ -22,7 +21,8 @@ namespace GodotTools.Build
public string ProjectFile { get; set; }
}
- [Signal] public event Action BuildStateChanged;
+ [Signal]
+ public delegate void BuildStateChangedEventHandler();
public bool HasBuildExited { get; private set; } = false;
@@ -58,7 +58,7 @@ namespace GodotTools.Build
}
// TODO Use List once we have proper serialization.
- private readonly Array<BuildIssue> _issues = new Array<BuildIssue>();
+ private Godot.Collections.Array<BuildIssue> _issues = new();
private ItemList _issuesList;
private PopupMenu _issuesListContextMenu;
private TextEdit _buildLog;
@@ -123,7 +123,7 @@ namespace GodotTools.Build
throw new IndexOutOfRangeException("Item list index out of range");
// Get correct issue idx from issue list
- int issueIndex = (int)(long)_issuesList.GetItemMetadata(idx);
+ int issueIndex = (int)_issuesList.GetItemMetadata(idx);
if (issueIndex < 0 || issueIndex >= _issues.Count)
throw new IndexOutOfRangeException("Issue index out of range");
@@ -133,7 +133,9 @@ namespace GodotTools.Build
if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File))
return;
- string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : _buildInfo.Solution.GetBaseDir();
+ string projectDir = !string.IsNullOrEmpty(issue.ProjectFile) ?
+ issue.ProjectFile.GetBaseDir() :
+ _buildInfo.Solution.GetBaseDir();
string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
@@ -412,6 +414,16 @@ namespace GodotTools.Build
{
// In case it didn't update yet. We don't want to have to serialize any pending output.
UpdateBuildLogText();
+
+ // NOTE:
+ // Currently, GodotTools is loaded in its own load context. This load context is not reloaded, but the script still are.
+ // Until that changes, we need workarounds like this one because events keep strong references to disposed objects.
+ BuildManager.BuildLaunchFailed -= BuildLaunchFailed;
+ BuildManager.BuildStarted -= BuildStarted;
+ BuildManager.BuildFinished -= BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred
+ BuildManager.StdOutputReceived -= StdOutputReceived;
+ BuildManager.StdErrorReceived -= StdErrorReceived;
}
public void OnAfterDeserialize()
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index 02e9d98647..655be0ab5e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -1,61 +1,93 @@
-using GodotTools.Core;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
+using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using GodotTools.BuildLogger;
-using GodotTools.Internals;
using GodotTools.Utils;
-using Directory = System.IO.Directory;
namespace GodotTools.Build
{
public static class BuildSystem
{
- private static string MonoWindowsBinDir
+ private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- get
- {
- string monoWinBinDir = Path.Combine(Internal.MonoWindowsInstallRoot, "bin");
+ string dotnetPath = DotNetFinder.FindDotNetExe();
- if (!Directory.Exists(monoWinBinDir))
- throw new FileNotFoundException("Cannot find the Windows Mono install bin directory.");
+ if (dotnetPath == null)
+ throw new FileNotFoundException("Cannot find the dotnet executable.");
- return monoWinBinDir;
- }
+ var startInfo = new ProcessStartInfo(dotnetPath);
+
+ BuildArguments(buildInfo, startInfo.ArgumentList);
+
+ string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
+ stdOutHandler?.Invoke(launchMessage);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine(launchMessage);
+
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
+ startInfo.UseShellExecute = false;
+ startInfo.CreateNoWindow = true;
+
+ // Needed when running from Developer Command Prompt for VS
+ RemovePlatformVariable(startInfo.EnvironmentVariables);
+
+ var process = new Process { StartInfo = startInfo };
+
+ if (stdOutHandler != null)
+ process.OutputDataReceived += (_, e) => stdOutHandler.Invoke(e.Data);
+ if (stdErrHandler != null)
+ process.ErrorDataReceived += (_, e) => stdErrHandler.Invoke(e.Data);
+
+ process.Start();
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ return process;
}
- private static Godot.EditorSettings EditorSettings =>
- GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ {
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ {
+ process.WaitForExit();
+
+ return process.ExitCode;
+ }
+ }
- private static bool UsingMonoMsBuildOnWindows
+ public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- get
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
- if (OS.IsWindows)
- {
- return (BuildTool)EditorSettings.GetSetting("mono/builds/build_tool")
- == BuildTool.MsBuildMono;
- }
+ await process.WaitForExitAsync();
- return false;
+ return process.ExitCode;
}
}
- private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ private static Process LaunchPublish(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- (string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
+ string dotnetPath = DotNetFinder.FindDotNetExe();
- if (msbuildPath == null)
- throw new FileNotFoundException("Cannot find the MSBuild executable.");
+ if (dotnetPath == null)
+ throw new FileNotFoundException("Cannot find the dotnet executable.");
- string compilerArgs = BuildArguments(buildTool, buildInfo);
+ var startInfo = new ProcessStartInfo(dotnetPath);
- var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
+ BuildPublishArguments(buildInfo, startInfo.ArgumentList);
- string launchMessage = $"Running: \"{startInfo.FileName}\" {startInfo.Arguments}";
+ string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
stdOutHandler?.Invoke(launchMessage);
if (Godot.OS.IsStdoutVerbose())
Console.WriteLine(launchMessage);
@@ -63,27 +95,16 @@ namespace GodotTools.Build
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
- startInfo.CreateNoWindow = true;
-
- if (UsingMonoMsBuildOnWindows)
- {
- // These environment variables are required for Mono's MSBuild to find the compilers.
- // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
- string monoWinBinDir = MonoWindowsBinDir;
- startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
- startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
- startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
- }
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
- var process = new Process {StartInfo = startInfo};
+ var process = new Process { StartInfo = startInfo };
if (stdOutHandler != null)
- process.OutputDataReceived += (s, e) => stdOutHandler.Invoke(e.Data);
+ process.OutputDataReceived += (_, e) => stdOutHandler.Invoke(e.Data);
if (stdErrHandler != null)
- process.ErrorDataReceived += (s, e) => stdErrHandler.Invoke(e.Data);
+ process.ErrorDataReceived += (_, e) => stdErrHandler.Invoke(e.Data);
process.Start();
@@ -93,9 +114,9 @@ namespace GodotTools.Build
return process;
}
- public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ public static int Publish(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ using (var process = LaunchPublish(buildInfo, stdOutHandler, stdErrHandler))
{
process.WaitForExit();
@@ -103,38 +124,101 @@ namespace GodotTools.Build
}
}
- public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ private static void BuildArguments(BuildInfo buildInfo, Collection<string> arguments)
{
- using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ // `dotnet clean` / `dotnet build` commands
+ arguments.Add(buildInfo.OnlyClean ? "clean" : "build");
+
+ // Solution
+ arguments.Add(buildInfo.Solution);
+
+ // `dotnet clean` doesn't recognize these options
+ if (!buildInfo.OnlyClean)
{
- await process.WaitForExitAsync();
+ // Restore
+ // `dotnet build` restores by default, unless requested not to
+ if (!buildInfo.Restore)
+ arguments.Add("--no-restore");
+
+ // Incremental or rebuild
+ if (buildInfo.Rebuild)
+ arguments.Add("--no-incremental");
+ }
- return process.ExitCode;
+ // Configuration
+ arguments.Add("-c");
+ arguments.Add(buildInfo.Configuration);
+
+ // Verbosity
+ arguments.Add("-v");
+ arguments.Add("normal");
+
+ // Logger
+ AddLoggerArgument(buildInfo, arguments);
+
+ // Custom properties
+ foreach (var customProperty in buildInfo.CustomProperties)
+ {
+ arguments.Add("-p:" + (string)customProperty);
}
}
- private static string BuildArguments(BuildTool buildTool, BuildInfo buildInfo)
+ private static void BuildPublishArguments(BuildInfo buildInfo, Collection<string> arguments)
{
- string arguments = string.Empty;
+ arguments.Add("publish"); // `dotnet publish` command
+
+ // Solution
+ arguments.Add(buildInfo.Solution);
+
+ // Restore
+ // `dotnet publish` restores by default, unless requested not to
+ if (!buildInfo.Restore)
+ arguments.Add("--no-restore");
- if (buildTool == BuildTool.DotnetCli)
- arguments += "msbuild"; // `dotnet msbuild` command
+ // Incremental or rebuild
+ if (buildInfo.Rebuild)
+ arguments.Add("--no-incremental");
- arguments += $@" ""{buildInfo.Solution}""";
+ // Configuration
+ arguments.Add("-c");
+ arguments.Add(buildInfo.Configuration);
- if (buildInfo.Restore)
- arguments += " /restore";
+ // Runtime Identifier
+ arguments.Add("-r");
+ arguments.Add(buildInfo.RuntimeIdentifier!);
- arguments += $@" /t:{string.Join(",", buildInfo.Targets)} " +
- $@"""/p:{"Configuration=" + buildInfo.Configuration}"" /v:normal " +
- $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{buildInfo.LogsDirPath}""";
+ // Self-published
+ arguments.Add("--self-contained");
+ arguments.Add("true");
- foreach (string customProperty in buildInfo.CustomProperties)
+ // Verbosity
+ arguments.Add("-v");
+ arguments.Add("normal");
+
+ // Logger
+ AddLoggerArgument(buildInfo, arguments);
+
+ // Custom properties
+ foreach (var customProperty in buildInfo.CustomProperties)
+ {
+ arguments.Add("-p:" + (string)customProperty);
+ }
+
+ // Publish output directory
+ if (buildInfo.PublishOutputDir != null)
{
- arguments += " /p:" + customProperty;
+ arguments.Add("-o");
+ arguments.Add(buildInfo.PublishOutputDir);
}
+ }
+
+ private static void AddLoggerArgument(BuildInfo buildInfo, Collection<string> arguments)
+ {
+ string buildLoggerPath = Path.Combine(Internals.GodotSharpDirs.DataEditorToolsDir,
+ "GodotTools.BuildLogger.dll");
- return arguments;
+ arguments.Add(
+ $"-l:{typeof(GodotBuildLogger).FullName},{buildLoggerPath};{buildInfo.LogsDirPath}");
}
private static void RemovePlatformVariable(StringDictionary environmentVariables)
@@ -145,7 +229,7 @@ namespace GodotTools.Build
foreach (string env in environmentVariables.Keys)
{
- if (env.ToUpper() == "PLATFORM")
+ if (env.ToUpperInvariant() == "PLATFORM")
platformEnvironmentVariables.Add(env);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
deleted file mode 100644
index 837c8adddb..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace GodotTools.Build
-{
- public enum BuildTool : long
- {
- MsBuildMono,
- MsBuildVs,
- JetBrainsMsBuild,
- DotnetCli
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
new file mode 100644
index 0000000000..7bce53308c
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using JetBrains.Annotations;
+using OS = GodotTools.Utils.OS;
+
+namespace GodotTools.Build
+{
+ public static class DotNetFinder
+ {
+ [CanBeNull]
+ public static string FindDotNetExe()
+ {
+ // In the future, this method may do more than just search in PATH. We could look in
+ // known locations or use Godot's linked nethost to search from the hostfxr location.
+
+ return OS.PathWhich("dotnet");
+ }
+
+ public static bool TryFindDotNetSdk(
+ Version expectedVersion,
+ [NotNullWhen(true)] out Version version,
+ [NotNullWhen(true)] out string path
+ )
+ {
+ version = null;
+ path = null;
+
+ string dotNetExe = FindDotNetExe();
+
+ if (string.IsNullOrEmpty(dotNetExe))
+ return false;
+
+ using Process process = new Process();
+ process.StartInfo = new ProcessStartInfo(dotNetExe, "--list-sdks")
+ {
+ UseShellExecute = false,
+ RedirectStandardOutput = true
+ };
+
+ process.StartInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en-US";
+
+ var lines = new List<string>();
+
+ process.OutputDataReceived += (_, e) =>
+ {
+ if (!string.IsNullOrWhiteSpace(e.Data))
+ lines.Add(e.Data);
+ };
+
+ try
+ {
+ process.Start();
+ }
+ catch
+ {
+ return false;
+ }
+
+ process.BeginOutputReadLine();
+ process.WaitForExit();
+
+ Version latestVersionMatch = null;
+ string matchPath = null;
+
+ foreach (var line in lines)
+ {
+ string[] sdkLineParts = line.Trim()
+ .Split(' ', 2, StringSplitOptions.TrimEntries);
+
+ if (sdkLineParts.Length < 2)
+ continue;
+
+ if (!Version.TryParse(sdkLineParts[0], out var lineVersion))
+ continue;
+
+ // We're looking for the exact same major version
+ if (lineVersion.Major != expectedVersion.Major)
+ continue;
+
+ if (latestVersionMatch != null && lineVersion < latestVersionMatch)
+ continue;
+
+ latestVersionMatch = lineVersion;
+ matchPath = sdkLineParts[1].TrimStart('[').TrimEnd(']');
+ }
+
+ if (latestVersionMatch == null)
+ return false;
+
+ version = latestVersionMatch;
+ path = Path.Combine(matchPath!, version.ToString());
+
+ return true;
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 3c020a2589..6dae0a3a0e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -1,13 +1,12 @@
using System;
using Godot;
using GodotTools.Internals;
-using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
namespace GodotTools.Build
{
- public class MSBuildPanel : VBoxContainer
+ public partial class MSBuildPanel : VBoxContainer
{
public BuildOutputView BuildOutputView { get; private set; }
@@ -28,7 +27,6 @@ namespace GodotTools.Build
BuildOutputView.UpdateIssuesList();
}
- [UsedImplicitly]
public void BuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -57,7 +55,6 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
- [UsedImplicitly]
private void RebuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -73,7 +70,7 @@ namespace GodotTools.Build
GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
}
- if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Rebuild" }))
+ if (!BuildManager.BuildProjectBlocking("Debug", rebuild: true))
return; // Build failed
// Notify running game for hot-reload
@@ -86,13 +83,12 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
- [UsedImplicitly]
private void CleanSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return; // No solution to build
- BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Clean" });
+ _ = BuildManager.CleanProjectBlocking("Debug");
}
private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
deleted file mode 100644
index a859c6f717..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ /dev/null
@@ -1,233 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Godot;
-using GodotTools.Ides.Rider;
-using GodotTools.Internals;
-using Directory = System.IO.Directory;
-using Environment = System.Environment;
-using File = System.IO.File;
-using Path = System.IO.Path;
-using OS = GodotTools.Utils.OS;
-
-namespace GodotTools.Build
-{
- public static class MsBuildFinder
- {
- private static string _msbuildToolsPath = string.Empty;
- private static string _msbuildUnixPath = string.Empty;
-
- public static (string, BuildTool) FindMsBuild()
- {
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
-
- if (OS.IsWindows)
- {
- switch (buildTool)
- {
- case BuildTool.DotnetCli:
- {
- string dotnetCliPath = OS.PathWhich("dotnet");
- if (!string.IsNullOrEmpty(dotnetCliPath))
- return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio.");
- goto case BuildTool.MsBuildVs;
- }
- case BuildTool.MsBuildVs:
- {
- if (string.IsNullOrEmpty(_msbuildToolsPath) || !File.Exists(_msbuildToolsPath))
- {
- // Try to search it again if it wasn't found last time or if it was removed from its location
- _msbuildToolsPath = FindMsBuildToolsPathOnWindows();
-
- if (string.IsNullOrEmpty(_msbuildToolsPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildVs}'.");
- }
-
- if (!_msbuildToolsPath.EndsWith("\\"))
- _msbuildToolsPath += "\\";
-
- return (Path.Combine(_msbuildToolsPath, "MSBuild.exe"), BuildTool.MsBuildVs);
- }
- case BuildTool.MsBuildMono:
- {
- string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat");
-
- if (!File.Exists(msbuildPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildMono}'. Tried with path: {msbuildPath}");
-
- return (msbuildPath, BuildTool.MsBuildMono);
- }
- case BuildTool.JetBrainsMsBuild:
- {
- string editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName);
-
- if (!File.Exists(editorPath))
- throw new FileNotFoundException($"Cannot find Rider executable. Tried with path: {editorPath}");
-
- var riderDir = new FileInfo(editorPath).Directory?.Parent;
-
- string msbuildPath = Path.Combine(riderDir.FullName, @"tools\MSBuild\Current\Bin\MSBuild.exe");
-
- if (!File.Exists(msbuildPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildJetBrains}'. Tried with path: {msbuildPath}");
-
- return (msbuildPath, BuildTool.JetBrainsMsBuild);
- }
- default:
- throw new IndexOutOfRangeException("Invalid build tool in editor settings");
- }
- }
-
- if (OS.IsUnixLike)
- {
- switch (buildTool)
- {
- case BuildTool.DotnetCli:
- {
- string dotnetCliPath = FindBuildEngineOnUnix("dotnet");
- if (!string.IsNullOrEmpty(dotnetCliPath))
- return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono.");
- goto case BuildTool.MsBuildMono;
- }
- case BuildTool.MsBuildMono:
- {
- if (string.IsNullOrEmpty(_msbuildUnixPath) || !File.Exists(_msbuildUnixPath))
- {
- // Try to search it again if it wasn't found last time or if it was removed from its location
- _msbuildUnixPath = FindBuildEngineOnUnix("msbuild");
- }
-
- if (string.IsNullOrEmpty(_msbuildUnixPath))
- throw new FileNotFoundException($"Cannot find binary for '{BuildManager.PropNameMSBuildMono}'");
-
- return (_msbuildUnixPath, BuildTool.MsBuildMono);
- }
- default:
- throw new IndexOutOfRangeException("Invalid build tool in editor settings");
- }
- }
-
- throw new PlatformNotSupportedException();
- }
-
- private static IEnumerable<string> MsBuildHintDirs
- {
- get
- {
- var result = new List<string>();
-
- if (OS.IsMacOS)
- {
- result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
- result.Add("/opt/local/bin/");
- result.Add("/usr/local/var/homebrew/linked/mono/bin/");
- result.Add("/usr/local/bin/");
- result.Add("/usr/local/bin/dotnet/");
- result.Add("/usr/local/share/dotnet/");
- }
-
- result.Add("/opt/novell/mono/bin/");
-
- return result;
- }
- }
-
- private static string FindBuildEngineOnUnix(string name)
- {
- string ret = OS.PathWhich(name);
-
- if (!string.IsNullOrEmpty(ret))
- return ret;
-
- string retFallback = OS.PathWhich($"{name}.exe");
-
- if (!string.IsNullOrEmpty(retFallback))
- return retFallback;
-
- foreach (string hintDir in MsBuildHintDirs)
- {
- string hintPath = Path.Combine(hintDir, name);
-
- if (File.Exists(hintPath))
- return hintPath;
- }
-
- return string.Empty;
- }
-
- private static string FindMsBuildToolsPathOnWindows()
- {
- if (!OS.IsWindows)
- throw new PlatformNotSupportedException();
-
- // Try to find 15.0 with vswhere
-
- string[] envNames = Internal.GodotIs32Bits() ?
- envNames = new[] { "ProgramFiles", "ProgramW6432" } :
- envNames = new[] { "ProgramFiles(x86)", "ProgramFiles" };
-
- string vsWherePath = null;
- foreach (var envName in envNames)
- {
- vsWherePath = Environment.GetEnvironmentVariable(envName);
- if (!string.IsNullOrEmpty(vsWherePath))
- {
- vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
- if (File.Exists(vsWherePath))
- break;
- }
-
- vsWherePath = null;
- }
-
- var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
-
- var outputArray = new Godot.Collections.Array<string>();
- int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
- output: (Godot.Collections.Array)outputArray);
-
- if (exitCode != 0)
- return string.Empty;
-
- if (outputArray.Count == 0)
- return string.Empty;
-
- var lines = outputArray[0].Split('\n');
-
- foreach (string line in lines)
- {
- int sepIdx = line.IndexOf(':');
-
- if (sepIdx <= 0)
- continue;
-
- string key = line.Substring(0, sepIdx); // No need to trim
-
- if (key != "installationPath")
- continue;
-
- string value = line.Substring(sepIdx + 1).StripEdges();
-
- if (string.IsNullOrEmpty(value))
- throw new FormatException("installationPath value is empty");
-
- if (!value.EndsWith("\\"))
- value += "\\";
-
- // Since VS2019, the directory is simply named "Current"
- string msbuildDir = Path.Combine(value, "MSBuild\\Current\\Bin");
-
- if (Directory.Exists(msbuildDir))
- return msbuildDir;
-
- // Directory name "15.0" is used in VS 2017
- return Path.Combine(value, "MSBuild\\15.0\\Bin");
- }
-
- return string.Empty;
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
index 63b97e981e..fdb86c8f34 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
@@ -39,7 +40,8 @@ namespace GodotTools.Build
// Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
- nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = "https://api.nuget.org/v3/index.json";
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value =
+ "https://api.nuget.org/v3/index.json";
nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
}
@@ -181,8 +183,8 @@ namespace GodotTools.Build
// - The sha512 of the nupkg is base64 encoded.
// - We can get the nuspec from the nupkg which is a Zip file.
- string packageIdLower = packageId.ToLower();
- string packageVersionLower = packageVersion.ToLower();
+ string packageIdLower = packageId.ToLowerInvariant();
+ string packageVersionLower = packageVersion.ToLowerInvariant();
string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
@@ -227,9 +229,11 @@ namespace GodotTools.Build
var nuspecEntry = archive.GetEntry(packageId + ".nuspec");
if (nuspecEntry == null)
- throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
+ throw new InvalidOperationException(
+ $"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
- nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath()));
+ nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name
+ .ToLowerInvariant().SimplifyGodotPath()));
// Extract the other package files
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index e9718cc82c..fc325fc25b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -75,8 +75,17 @@ namespace GodotTools.Export
}
else
{
- string bits = features.Contains("64") ? "64" : features.Contains("32") ? "32" : null;
- CompileAssembliesForDesktop(exporter, platform, isDebug, bits, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir);
+ string arch = "";
+ if (features.Contains("x86_64")) {
+ arch = "x86_64";
+ } else if (features.Contains("x86_32")) {
+ arch = "x86_32";
+ } else if (features.Contains("arm64")) {
+ arch = "arm64";
+ } else if (features.Contains("arm32")) {
+ arch = "arm32";
+ }
+ CompileAssembliesForDesktop(exporter, platform, isDebug, arch, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir);
}
}
@@ -112,7 +121,7 @@ namespace GodotTools.Export
}
}
- public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string bits, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir)
+ public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string arch, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir)
{
foreach (var assembly in assemblies)
{
@@ -126,9 +135,9 @@ namespace GodotTools.Export
string outputFileName = assemblyName + ".dll" + outputFileExtension;
string tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
- var compilerArgs = GetAotCompilerArgs(platform, isDebug, bits, aotOpts, assemblyPath, tempOutputFilePath);
+ var compilerArgs = GetAotCompilerArgs(platform, isDebug, arch, aotOpts, assemblyPath, tempOutputFilePath);
- string compilerDirPath = GetMonoCrossDesktopDirName(platform, bits);
+ string compilerDirPath = GetMonoCrossDesktopDirName(platform, arch);
ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
@@ -289,7 +298,7 @@ MONO_AOT_MODE_LAST = 1000,
// Archive the AOT object files into a static library
var arFilePathsForAllArchs = new List<string>();
- string projectAssemblyName = GodotSharpEditor.ProjectAssemblyName;
+ string projectAssemblyName = GodotSharpDirs.ProjectAssemblyName;
foreach (var archPathsPair in objFilePathsForiOSArch)
{
@@ -432,9 +441,9 @@ MONO_AOT_MODE_LAST = 1000,
var androidToolPrefixes = new Dictionary<string, string>
{
- ["armeabi-v7a"] = "arm-linux-androideabi-",
- ["arm64-v8a"] = "aarch64-linux-android-",
- ["x86"] = "i686-linux-android-",
+ ["arm32"] = "arm-linux-androideabi-",
+ ["arm64"] = "aarch64-linux-android-",
+ ["x86_32"] = "i686-linux-android-",
["x86_64"] = "x86_64-linux-android-"
};
@@ -547,9 +556,9 @@ MONO_AOT_MODE_LAST = 1000,
{
var androidAbis = new[]
{
- "armeabi-v7a",
- "arm64-v8a",
- "x86",
+ "arm32",
+ "arm64",
+ "x86_32",
"x86_64"
};
@@ -560,9 +569,9 @@ MONO_AOT_MODE_LAST = 1000,
{
var abiArchs = new Dictionary<string, string>
{
- ["armeabi-v7a"] = "armv7",
- ["arm64-v8a"] = "aarch64-v8a",
- ["x86"] = "i686",
+ ["arm32"] = "armv7",
+ ["arm64"] = "aarch64-v8a",
+ ["x86_32"] = "i686",
["x86_64"] = "x86_64"
};
@@ -571,31 +580,25 @@ MONO_AOT_MODE_LAST = 1000,
return $"{arch}-linux-android";
}
- private static string GetMonoCrossDesktopDirName(string platform, string bits)
+ private static string GetMonoCrossDesktopDirName(string platform, string arch)
{
switch (platform)
{
case OS.Platforms.Windows:
case OS.Platforms.UWP:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"windows-{arch}";
}
case OS.Platforms.MacOS:
{
- Debug.Assert(bits == null || bits == "64");
- string arch = "x86_64";
return $"{platform}-{arch}";
}
case OS.Platforms.LinuxBSD:
- case OS.Platforms.Server:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"linux-{arch}";
}
case OS.Platforms.Haiku:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"{platform}-{arch}";
}
default:
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index cca18a2a1f..e1b5530b93 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -4,11 +4,9 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Runtime.CompilerServices;
using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
-using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
@@ -17,61 +15,13 @@ using Path = System.IO.Path;
namespace GodotTools.Export
{
- public class ExportPlugin : EditorExportPlugin
+ public partial class ExportPlugin : EditorExportPlugin
{
- [Flags]
- private enum I18NCodesets : long
- {
- None = 0,
- CJK = 1,
- MidEast = 2,
- Other = 4,
- Rare = 8,
- West = 16,
- All = CJK | MidEast | Other | Rare | West
- }
-
- private string _maybeLastExportError;
-
- private void AddI18NAssemblies(Godot.Collections.Dictionary<string, string> assemblies, string bclDir)
- {
- var codesets = (I18NCodesets)ProjectSettings.GetSetting("mono/export/i18n_codesets");
-
- if (codesets == I18NCodesets.None)
- return;
-
- void AddI18NAssembly(string name) => assemblies.Add(name, Path.Combine(bclDir, $"{name}.dll"));
-
- AddI18NAssembly("I18N");
-
- if ((codesets & I18NCodesets.CJK) != 0)
- AddI18NAssembly("I18N.CJK");
- if ((codesets & I18NCodesets.MidEast) != 0)
- AddI18NAssembly("I18N.MidEast");
- if ((codesets & I18NCodesets.Other) != 0)
- AddI18NAssembly("I18N.Other");
- if ((codesets & I18NCodesets.Rare) != 0)
- AddI18NAssembly("I18N.Rare");
- if ((codesets & I18NCodesets.West) != 0)
- AddI18NAssembly("I18N.West");
- }
-
public void RegisterExportSettings()
{
// TODO: These would be better as export preset options, but that doesn't seem to be supported yet
GlobalDef("mono/export/include_scripts_content", false);
- GlobalDef("mono/export/export_assemblies_inside_pck", true);
-
- GlobalDef("mono/export/i18n_codesets", I18NCodesets.All);
-
- ProjectSettings.AddPropertyInfo(new Godot.Collections.Dictionary
- {
- ["type"] = Variant.Type.Int,
- ["name"] = "mono/export/i18n_codesets",
- ["hint"] = PropertyHint.Flags,
- ["hint_string"] = "CJK,MidEast,Other,Rare,West"
- });
GlobalDef("mono/export/aot/enabled", false);
GlobalDef("mono/export/aot/full_aot", false);
@@ -85,11 +35,7 @@ namespace GodotTools.Export
GlobalDef("mono/export/aot/android_toolchain_path", "");
}
- private void AddFile(string srcPath, string dstPath, bool remap = false)
- {
- // Add file to the PCK
- AddFile(dstPath.Replace("\\", "/"), File.ReadAllBytes(srcPath), remap);
- }
+ private string _maybeLastExportError;
// With this method we can override how a file is exported in the PCK
public override void _ExportFile(string path, string type, string[] features)
@@ -100,7 +46,9 @@ namespace GodotTools.Export
return;
if (Path.GetExtension(path) != Internal.CSharpLanguageExtension)
- throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
+ throw new ArgumentException(
+ $"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}",
+ nameof(path));
// TODO What if the source file is not part of the game's C# project
@@ -152,159 +100,93 @@ namespace GodotTools.Export
if (!DeterminePlatformFromFeatures(features, out string platform))
throw new NotSupportedException("Target platform not supported");
+ if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS }
+ .Contains(platform))
+ {
+ throw new NotImplementedException("Target platform not yet implemented");
+ }
+
string outputDir = new FileInfo(path).Directory?.FullName ??
- throw new FileNotFoundException("Base directory not found");
+ throw new FileNotFoundException("Output base directory not found");
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
- if (!BuildManager.BuildProjectBlocking(buildConfig, platform: platform))
- throw new Exception("Failed to build project");
+ // TODO: This works for now, as we only implemented support for x86 family desktop so far, but it needs to be fixed
+ string arch = features.Contains("64") ? "x86_64" : "x86";
- // Add dependency assemblies
+ string ridOS = DetermineRuntimeIdentifierOS(platform);
+ string ridArch = DetermineRuntimeIdentifierArch(arch);
+ string runtimeIdentifier = $"{ridOS}-{ridArch}";
- var assemblies = new Godot.Collections.Dictionary<string, string>();
+ // Create temporary publish output directory
- string projectDllName = GodotSharpEditor.ProjectAssemblyName;
- string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
- string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
+ string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
+ $"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}");
- assemblies[projectDllName] = projectDllSrcPath;
+ if (!Directory.Exists(publishOutputTempDir))
+ Directory.CreateDirectory(publishOutputTempDir);
- string bclDir = DeterminePlatformBclDir(platform);
+ // Execute dotnet publish
- if (platform == OS.Platforms.Android)
+ if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
+ runtimeIdentifier, publishOutputTempDir))
{
- string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext");
- string monoAndroidAssemblyPath = Path.Combine(godotAndroidExtProfileDir, "Mono.Android.dll");
+ throw new Exception("Failed to build project");
+ }
- if (!File.Exists(monoAndroidAssemblyPath))
- throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
+ string soExt = ridOS switch
+ {
+ OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
+ OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
+ _ => "so"
+ };
- assemblies["Mono.Android"] = monoAndroidAssemblyPath;
- }
- else if (platform == OS.Platforms.HTML5)
+ if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
+ // NativeAOT shared library output
+ && !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
{
- // Ideally these would be added automatically since they're referenced by the wasm BCL assemblies.
- // However, at least in the case of 'WebAssembly.Net.Http' for some reason the BCL assemblies
- // reference a different version even though the assembly is the same, for some weird reason.
-
- var wasmFrameworkAssemblies = new[] { "WebAssembly.Bindings", "WebAssembly.Net.WebSockets" };
-
- foreach (string thisWasmFrameworkAssemblyName in wasmFrameworkAssemblies)
- {
- string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll");
- if (!File.Exists(thisWasmFrameworkAssemblyPath))
- throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath);
- assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath;
- }
-
- // Assemblies that can have a different name in a newer version. Newer version must come first and it has priority.
- (string newName, string oldName)[] wasmFrameworkAssembliesOneOf = new[]
- {
- ("System.Net.Http.WebAssemblyHttpHandler", "WebAssembly.Net.Http")
- };
-
- foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf)
- {
- string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
- if (File.Exists(thisWasmFrameworkAssemblyPath))
- {
- assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath;
- }
- else
- {
- thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
- if (!File.Exists(thisWasmFrameworkAssemblyPath))
- {
- throw new FileNotFoundException("Expected one of the following assemblies but none were found: " +
- $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
- thisWasmFrameworkAssemblyPath);
- }
-
- assemblies[thisWasmFrameworkAssemblyName.oldName] = thisWasmFrameworkAssemblyPath;
- }
- }
+ throw new NotSupportedException(
+ "Publish succeeded but project assembly not found in the output directory");
}
- var initialAssemblies = assemblies.Duplicate();
- internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
-
- AddI18NAssemblies(assemblies, bclDir);
-
- string outputDataDir = null;
+ // Copy all files from the dotnet publish output directory to
+ // a data directory next to the Godot output executable.
- if (PlatformHasTemplateDir(platform))
- outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
+ string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject());
- string apiConfig = isDebug ? "Debug" : "Release";
- string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
+ if (Directory.Exists(outputDataDir))
+ Directory.Delete(outputDataDir, recursive: true); // Clean first
- bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null;
+ Directory.CreateDirectory(outputDataDir);
- if (!assembliesInsidePck)
+ foreach (string dir in Directory.GetDirectories(publishOutputTempDir, "*", SearchOption.AllDirectories))
{
- string outputDataGameAssembliesDir = Path.Combine(outputDataDir, "Assemblies");
- if (!Directory.Exists(outputDataGameAssembliesDir))
- Directory.CreateDirectory(outputDataGameAssembliesDir);
+ Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(publishOutputTempDir.Length + 1)));
}
- foreach (var assembly in assemblies)
+ foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
{
- void AddToAssembliesDir(string fileSrcPath)
- {
- if (assembliesInsidePck)
- {
- string fileDstPath = Path.Combine(resAssembliesDir, fileSrcPath.GetFile());
- AddFile(fileSrcPath, fileDstPath);
- }
- else
- {
- Debug.Assert(outputDataDir != null);
- string fileDstPath = Path.Combine(outputDataDir, "Assemblies", fileSrcPath.GetFile());
- File.Copy(fileSrcPath, fileDstPath);
- }
- }
-
- string assemblySrcPath = assembly.Value;
-
- string assemblyPathWithoutExtension = Path.ChangeExtension(assemblySrcPath, null);
- string pdbSrcPath = assemblyPathWithoutExtension + ".pdb";
-
- AddToAssembliesDir(assemblySrcPath);
-
- if (File.Exists(pdbSrcPath))
- AddToAssembliesDir(pdbSrcPath);
+ File.Copy(file, Path.Combine(outputDataDir, file.Substring(publishOutputTempDir.Length + 1)));
}
+ }
- // AOT compilation
- bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
+ private string DetermineRuntimeIdentifierOS(string platform)
+ => OS.DotNetOSPlatformMap[platform];
- if (aotEnabled)
+ private string DetermineRuntimeIdentifierArch(string arch)
+ {
+ return arch switch
{
- string aotToolchainPath = null;
-
- if (platform == OS.Platforms.Android)
- aotToolchainPath = (string)ProjectSettings.GetSetting("mono/export/aot/android_toolchain_path");
-
- if (aotToolchainPath == string.Empty)
- aotToolchainPath = null; // Don't risk it being used as current working dir
-
- // TODO: LLVM settings are hard-coded and disabled for now
- var aotOpts = new AotOptions
- {
- EnableLLVM = false,
- LLVMOnly = false,
- LLVMPath = "",
- LLVMOutputPath = "",
- FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
- UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"),
- ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? Array.Empty<string>(),
- ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? Array.Empty<string>(),
- ToolchainPath = aotToolchainPath
- };
-
- AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies);
- }
+ "x86" => "x86",
+ "x86_32" => "x86",
+ "x64" => "x64",
+ "x86_64" => "x64",
+ "armeabi-v7a" => "arm",
+ "arm64-v8a" => "arm64",
+ "armv7" => "arm",
+ "arm64" => "arm64",
+ _ => throw new ArgumentOutOfRangeException(nameof(arch), arch, "Unexpected architecture")
+ };
}
public override void _ExportEnd()
@@ -316,8 +198,10 @@ namespace GodotTools.Export
if (Directory.Exists(aotTempDir))
Directory.Delete(aotTempDir, recursive: true);
- // TODO: Just a workaround until the export plugins can be made to abort with errors
- if (!string.IsNullOrEmpty(_maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
+ // TODO: The following is just a workaround until the export plugins can be made to abort with errors
+
+ // We check for empty as well, because it's set to empty after hot-reloading
+ if (!string.IsNullOrEmpty(_maybeLastExportError))
{
string lastExportError = _maybeLastExportError;
_maybeLastExportError = null;
@@ -326,69 +210,11 @@ namespace GodotTools.Export
}
}
- [NotNull]
- private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
- {
- string target = isDebug ? "release_debug" : "release";
-
- // NOTE: Bits is ok for now as all platforms with a data directory only have one or two architectures.
- // However, this may change in the future if we add arm linux or windows desktop templates.
- string bits = features.Contains("64") ? "64" : "32";
-
- string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
-
- string templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
- bool validTemplatePathFound = true;
-
- if (!Directory.Exists(templateDirPath))
- {
- validTemplatePathFound = false;
-
- if (isDebug)
- {
- target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
- templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
- validTemplatePathFound = true;
-
- if (!Directory.Exists(templateDirPath))
- validTemplatePathFound = false;
- }
- }
-
- if (!validTemplatePathFound)
- throw new FileNotFoundException("Data template directory not found", templateDirPath);
-
- string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject());
-
- if (Directory.Exists(outputDataDir))
- Directory.Delete(outputDataDir, recursive: true); // Clean first
-
- Directory.CreateDirectory(outputDataDir);
-
- foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
- {
- Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
- }
-
- foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
- {
- File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
- }
-
- return outputDataDir;
- }
-
- private static bool PlatformHasTemplateDir(string platform)
- {
- // macOS export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
- return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
- }
-
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
{
foreach (var feature in features)
{
- if (OS.PlatformNameMap.TryGetValue(feature, out platform))
+ if (OS.PlatformFeatureMap.TryGetValue(feature, out platform))
return true;
}
@@ -396,87 +222,11 @@ namespace GodotTools.Export
return false;
}
- private static string GetBclProfileDir(string profile)
- {
- string templatesDir = Internal.FullExportTemplatesDir;
- return Path.Combine(templatesDir, "bcl", profile);
- }
-
- private static string DeterminePlatformBclDir(string platform)
- {
- string templatesDir = Internal.FullExportTemplatesDir;
- string platformBclDir = Path.Combine(templatesDir, "bcl", platform);
-
- if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
- {
- string profile = DeterminePlatformBclProfile(platform);
- platformBclDir = Path.Combine(templatesDir, "bcl", profile);
-
- if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
- {
- if (PlatformRequiresCustomBcl(platform))
- throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
-
- platformBclDir = typeof(object).Assembly.Location.GetBaseDir(); // Use the one we're running on
- }
- }
-
- return platformBclDir;
- }
-
- /// <summary>
- /// Determines whether the BCL bundled with the Godot editor can be used for the target platform,
- /// or if it requires a custom BCL that must be distributed with the export templates.
- /// </summary>
- private static bool PlatformRequiresCustomBcl(string platform)
- {
- if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform))
- return true;
-
- // The 'net_4_x' BCL is not compatible between Windows and the other platforms.
- // We use the names 'net_4_x_win' and 'net_4_x' to differentiate between the two.
-
- bool isWinOrUwp = new[]
- {
- OS.Platforms.Windows,
- OS.Platforms.UWP
- }.Contains(platform);
-
- return OS.IsWindows ? !isWinOrUwp : isWinOrUwp;
- }
-
- private static string DeterminePlatformBclProfile(string platform)
- {
- switch (platform)
- {
- case OS.Platforms.Windows:
- case OS.Platforms.UWP:
- return "net_4_x_win";
- case OS.Platforms.MacOS:
- case OS.Platforms.LinuxBSD:
- case OS.Platforms.Server:
- case OS.Platforms.Haiku:
- return "net_4_x";
- case OS.Platforms.Android:
- return "monodroid";
- case OS.Platforms.iOS:
- return "monotouch";
- case OS.Platforms.HTML5:
- return "wasm";
- default:
- throw new NotSupportedException($"Platform not supported: {platform}");
- }
- }
-
private static string DetermineDataDirNameForProject()
{
string appName = (string)ProjectSettings.GetSetting("application/config/name");
string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies,
- string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies);
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index b39c3d1c0d..0aca60dad4 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -13,13 +13,14 @@ using GodotTools.Internals;
using GodotTools.ProjectEditor;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
+using Environment = System.Environment;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
using Path = System.IO.Path;
namespace GodotTools
{
- public class GodotSharpEditor : EditorPlugin, ISerializationListener
+ public partial class GodotSharpEditor : EditorPlugin, ISerializationListener
{
private EditorSettings _editorSettings;
@@ -39,28 +40,27 @@ namespace GodotTools
public bool SkipBuildBeforePlaying { get; set; } = false;
- public static string ProjectAssemblyName
+ [UsedImplicitly]
+ private bool CreateProjectSolutionIfNeeded()
{
- get
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath) || !File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
- string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
- projectAssemblyName = projectAssemblyName.ToSafeDirName();
- if (string.IsNullOrEmpty(projectAssemblyName))
- projectAssemblyName = "UnnamedProject";
- return projectAssemblyName;
+ return CreateProjectSolution();
}
+
+ return true;
}
private bool CreateProjectSolution()
{
- using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
+ using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
{
pr.Step("Generating C# project...".TTR());
string resourceDir = ProjectSettings.GlobalizePath("res://");
string path = resourceDir;
- string name = ProjectAssemblyName;
+ string name = GodotSharpDirs.ProjectAssemblyName;
string guid = CsProjOperations.GenerateGameProject(path, name);
@@ -75,7 +75,7 @@ namespace GodotTools
{
Guid = guid,
PathRelativeToSolution = name + ".csproj",
- Configs = new List<string> {"Debug", "ExportDebug", "ExportRelease"}
+ Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
};
solution.AddNewProject(name, projectInfo);
@@ -90,24 +90,6 @@ namespace GodotTools
return false;
}
- pr.Step("Updating Godot API assemblies...".TTR());
-
- string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug");
-
- if (!string.IsNullOrEmpty(debugApiAssembliesError))
- {
- ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError);
- return false;
- }
-
- string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release");
-
- if (!string.IsNullOrEmpty(releaseApiAssembliesError))
- {
- ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError);
- return false;
- }
-
pr.Step("Done".TTR());
// Here, after all calls to progress_task_step
@@ -141,7 +123,8 @@ namespace GodotTools
try
{
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ fallbackFolder);
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
}
catch (Exception e)
@@ -167,13 +150,6 @@ namespace GodotTools
Instance.MSBuildPanel.BuildSolution();
}
- public override void _Ready()
- {
- base._Ready();
-
- MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
- }
-
private enum MenuOptions
{
CreateSln,
@@ -197,7 +173,7 @@ namespace GodotTools
[UsedImplicitly]
public Error OpenInExternalEditor(Script script, int line, int col)
{
- var editorId = (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor");
+ var editorId = (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor");
switch (editorId)
{
@@ -219,13 +195,15 @@ namespace GodotTools
try
{
if (Godot.OS.IsStdoutVerbose())
- Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
+ Console.WriteLine(
+ $"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
OS.RunProcess(command, args);
}
catch (Exception e)
{
- GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
+ GD.PushError(
+ $"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
}
break;
@@ -347,7 +325,8 @@ namespace GodotTools
[UsedImplicitly]
public bool OverridesExternalEditor()
{
- return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
+ return (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor") !=
+ ExternalEditorId.None;
}
public override bool _Build()
@@ -368,7 +347,7 @@ namespace GodotTools
// NOTE: The order in which changes are made to the project is important
// Migrate to MSBuild project Sdks style if using the old style
- ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, ProjectAssemblyName);
+ ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
@@ -400,18 +379,49 @@ namespace GodotTools
throw new InvalidOperationException();
Instance = this;
+ var dotNetSdkSearchVersion = Environment.Version;
+
+ // First we try to find the .NET Sdk ourselves to make sure we get the
+ // correct version first (`RegisterDefaults` always picks the latest).
+ if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, out var sdkVersion, out string sdkPath))
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
+
+ ProjectUtils.MSBuildLocatorRegisterMSBuildPath(sdkPath);
+ }
+ else
+ {
+ try
+ {
+ ProjectUtils.MSBuildLocatorRegisterDefaults(out sdkVersion, out sdkPath);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
+ }
+ catch (InvalidOperationException e)
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ GD.PrintErr(e.ToString());
+ GD.PushError($".NET Sdk not found. The required version is '{dotNetSdkSearchVersion}'.");
+ }
+ }
+
var editorInterface = GetEditorInterface();
var editorBaseControl = editorInterface.GetBaseControl();
_editorSettings = editorInterface.GetEditorSettings();
+ GodotSharpDirs.RegisterProjectSettings();
+
_errorDialog = new AcceptDialog();
editorBaseControl.AddChild(_errorDialog);
MSBuildPanel = new MSBuildPanel();
+ MSBuildPanel.Ready += () =>
+ MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
_bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
- AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
+ AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" });
_menuPopup = new PopupMenu();
_menuPopup.Hide();
@@ -472,9 +482,9 @@ namespace GodotTools
_editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
- ["type"] = Variant.Type.Int,
+ ["type"] = (int)Variant.Type.Int,
["name"] = "mono/editor/external_editor",
- ["hint"] = PropertyHint.Enum,
+ ["hint"] = (int)PropertyHint.Enum,
["hint_string"] = settingsHintStr
});
@@ -487,7 +497,8 @@ namespace GodotTools
try
{
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ NuGetUtils.GodotFallbackFolderPath);
}
catch (Exception e)
{
@@ -503,20 +514,23 @@ namespace GodotTools
protected override void Dispose(bool disposing)
{
- base.Dispose(disposing);
-
- if (_exportPluginWeak != null)
+ if (disposing)
{
- // We need to dispose our export plugin before the editor destroys EditorSettings.
- // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
- // will be freed after EditorSettings already was, and its device polling thread
- // will try to access the EditorSettings singleton, resulting in null dereferencing.
- (_exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
+ if (IsInstanceValid(_exportPluginWeak))
+ {
+ // We need to dispose our export plugin before the editor destroys EditorSettings.
+ // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
+ // will be freed after EditorSettings already was, and its device polling thread
+ // will try to access the EditorSettings singleton, resulting in null dereferencing.
+ (_exportPluginWeak.GetRef().AsGodotObject() as ExportPlugin)?.Dispose();
+
+ _exportPluginWeak.Dispose();
+ }
- _exportPluginWeak.Dispose();
+ GodotIdeManager?.Dispose();
}
- GodotIdeManager?.Dispose();
+ base.Dispose(disposing);
}
public void OnBeforeSerialize()
@@ -533,8 +547,10 @@ namespace GodotTools
public static GodotSharpEditor Instance { get; private set; }
[UsedImplicitly]
- private GodotSharpEditor()
+ private static IntPtr InternalCreateInstance(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
{
+ Internal.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+ return new GodotSharpEditor().NativeInstance;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index f1d45463c5..30525ba04a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -1,14 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
- <TargetFramework>net472</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ <LangVersion>10</LangVersion>
<!-- The Godot editor uses the Debug Godot API assemblies -->
<GodotApiConfiguration>Debug</GodotApiConfiguration>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
<GodotApiAssembliesDir>$(GodotOutputDataDir)/Api/$(GodotApiConfiguration)</GodotApiAssembliesDir>
+ <ProduceReferenceAssembly>false</ProduceReferenceAssembly>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
+ <!-- Needed for our source generators to work despite this not being a Godot game project -->
+ <PropertyGroup>
+ <IsGodotToolsProject>true</IsGodotToolsProject>
+ </PropertyGroup>
+ <ItemGroup>
+ <CompilerVisibleProperty Include="IsGodotToolsProject" />
+ </ItemGroup>
<PropertyGroup Condition=" Exists('$(GodotApiAssembliesDir)/GodotSharp.dll') ">
<!-- The project is part of the Godot source tree -->
<!-- Use the Godot source tree output folder instead of '$(ProjectDir)/bin' -->
@@ -20,6 +30,8 @@
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+ <!-- For RiderPathLocator -->
+ <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>
@@ -30,6 +42,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <ProjectReference Include="..\..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+ <ItemGroup>
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj" />
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index dd05f28af0..260d13a714 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -1,10 +1,11 @@
using Godot;
using GodotTools.Internals;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
namespace GodotTools
{
- public class HotReloadAssemblyWatcher : Node
+ public partial class HotReloadAssemblyWatcher : Node
{
private Timer _watchTimer;
@@ -25,6 +26,7 @@ namespace GodotTools
Internal.ReloadAssemblies(softReload: false);
}
+ [UsedImplicitly]
public void RestartTimer()
{
_watchTimer.Stop();
@@ -38,7 +40,7 @@ namespace GodotTools
_watchTimer = new Timer
{
OneShot = false,
- WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
+ WaitTime = 0.5f
};
_watchTimer.Timeout += TimerTimeout;
AddChild(_watchTimer);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 23339fe50b..95b60aded1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -8,7 +8,7 @@ using GodotTools.Internals;
namespace GodotTools.Ides
{
- public sealed class GodotIdeManager : Node, ISerializationListener
+ public sealed partial class GodotIdeManager : Node, ISerializationListener
{
private MessagingServer _messagingServer;
@@ -76,7 +76,7 @@ namespace GodotTools.Ides
public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000)
{
- var editorId = (ExternalEditorId)GodotSharpEditor.Instance.GetEditorInterface()
+ var editorId = (ExternalEditorId)(int)GodotSharpEditor.Instance.GetEditorInterface()
.GetEditorSettings().GetSetting("mono/editor/external_editor");
string editorIdentity = GetExternalEditorIdentity(editorId);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index 71055f0125..4caab035de 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using System.Runtime.Versioning;
using Godot;
-using JetBrains.Annotations;
using Microsoft.Win32;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
@@ -113,6 +114,7 @@ namespace GodotTools.Ides.Rider
return installInfos.ToArray();
}
+ [SupportedOSPlatform("windows")]
private static RiderInfo[] CollectRiderInfosWindows()
{
var installInfos = new List<RiderInfo>();
@@ -217,6 +219,7 @@ namespace GodotTools.Ides.Rider
throw new Exception("Unknown OS.");
}
+ [SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
@@ -229,6 +232,7 @@ namespace GodotTools.Ides.Rider
}
}
+ [SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
{
if (key == null) return;
@@ -324,7 +328,7 @@ namespace GodotTools.Ides.Rider
{
public string install_location;
- [CanBeNull]
+ [return: MaybeNull]
public static string GetInstallLocationFromJson(string json)
{
try
@@ -378,7 +382,7 @@ namespace GodotTools.Ides.Rider
public string version;
public string versionSuffix;
- [CanBeNull]
+ [return: MaybeNull]
internal static ProductInfo GetProductInfo(string json)
{
try
@@ -402,7 +406,7 @@ namespace GodotTools.Ides.Rider
// ReSharper disable once InconsistentNaming
public ActiveApplication active_application;
- [CanBeNull]
+ [return: MaybeNull]
public static string GetLatestBuildFromJson(string json)
{
try
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index 3440eb701c..60602a5847 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -22,7 +22,7 @@ namespace GodotTools.Ides.Rider
public static void Initialize()
{
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var editor = (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor");
+ var editor = (ExternalEditorId)(int)editorSettings.GetSetting("mono/editor/external_editor");
if (editor == ExternalEditorId.Rider)
{
if (!editorSettings.HasSetting(EditorPathSettingName))
@@ -30,9 +30,9 @@ namespace GodotTools.Ides.Rider
Globals.EditorDef(EditorPathSettingName, "Optional");
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
- ["type"] = Variant.Type.String,
+ ["type"] = (int)Variant.Type.String,
["name"] = EditorPathSettingName,
- ["hint"] = PropertyHint.File,
+ ["hint"] = (int)PropertyHint.File,
["hint_string"] = ""
});
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
index 70ba7c733a..8f39ad063e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
@@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using Godot;
+using Godot.NativeInterop;
namespace GodotTools.Internals
{
@@ -8,19 +9,12 @@ namespace GodotTools.Internals
{
public string Task { get; }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_Create(string task, string label, int amount, bool canCancel);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_Dispose(string task);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_Step(string task, string state, int step, bool forceRefresh);
-
public EditorProgress(string task, string label, int amount, bool canCancel = false)
{
Task = task;
- internal_Create(task, label, amount, canCancel);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(task);
+ using godot_string labelIn = Marshaling.ConvertStringToNative(label);
+ Internal.godot_icall_EditorProgress_Create(taskIn, labelIn, amount, canCancel);
}
~EditorProgress()
@@ -33,18 +27,23 @@ namespace GodotTools.Internals
public void Dispose()
{
- internal_Dispose(Task);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ Internal.godot_icall_EditorProgress_Dispose(taskIn);
GC.SuppressFinalize(this);
}
public void Step(string state, int step = -1, bool forceRefresh = true)
{
- internal_Step(Task, state, step, forceRefresh);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ using godot_string stateIn = Marshaling.ConvertStringToNative(state);
+ Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
public bool TryStep(string state, int step = -1, bool forceRefresh = true)
{
- return internal_Step(Task, state, step, forceRefresh);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ using godot_string stateIn = Marshaling.ConvertStringToNative(state);
+ return Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index 5c5ced8c29..acb7cc3ab0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -1,3 +1,4 @@
+using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -5,35 +6,41 @@ namespace GodotTools.Internals
{
public static class Globals
{
- public static float EditorScale => internal_EditorScale();
-
- public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
- internal_GlobalDef(setting, defaultValue, restartIfChanged);
-
- public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
- internal_EditorDef(setting, defaultValue, restartIfChanged);
-
- public static object EditorShortcut(string setting) =>
- internal_EditorShortcut(setting);
+ public static float EditorScale => Internal.godot_icall_Globals_EditorScale();
+
+ public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
+ Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
+ using (result)
+ return Marshaling.ConvertVariantToManagedObject(result);
+ }
+
+ public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
+ Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
+ using (result)
+ return Marshaling.ConvertVariantToManagedObject(result);
+ }
+
+ public static object EditorShortcut(string setting)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result);
+ using (result)
+ return Marshaling.ConvertVariantToManagedObject(result);
+ }
[SuppressMessage("ReSharper", "InconsistentNaming")]
- public static string TTR(this string text) => internal_TTR(text);
-
- // Internal Calls
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern float internal_EditorScale();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_EditorShortcut(string setting);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_TTR(string text);
+ public static string TTR(this string text)
+ {
+ using godot_string textIn = Marshaling.ConvertStringToNative(text);
+ Internal.godot_icall_Globals_TTR(textIn, out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
index 5e70c399b2..14285cc0f1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
@@ -1,103 +1,125 @@
-using System.Runtime.CompilerServices;
+using System.IO;
+using Godot;
+using Godot.NativeInterop;
+using GodotTools.Core;
+using static GodotTools.Internals.Globals;
namespace GodotTools.Internals
{
public static class GodotSharpDirs
{
- public static string ResDataDir => internal_ResDataDir();
- public static string ResMetadataDir => internal_ResMetadataDir();
- public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir();
- public static string ResAssembliesDir => internal_ResAssembliesDir();
- public static string ResConfigDir => internal_ResConfigDir();
- public static string ResTempDir => internal_ResTempDir();
- public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir();
- public static string ResTempAssembliesDir => internal_ResTempAssembliesDir();
-
- public static string MonoUserDir => internal_MonoUserDir();
- public static string MonoLogsDir => internal_MonoLogsDir();
-
- #region Tools-only
- public static string MonoSolutionsDir => internal_MonoSolutionsDir();
- public static string BuildLogsDirs => internal_BuildLogsDirs();
-
- public static string ProjectSlnPath => internal_ProjectSlnPath();
- public static string ProjectCsProjPath => internal_ProjectCsProjPath();
-
- public static string DataEditorToolsDir => internal_DataEditorToolsDir();
- public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir();
- #endregion
-
- public static string DataMonoEtcDir => internal_DataMonoEtcDir();
- public static string DataMonoLibDir => internal_DataMonoLibDir();
-
- #region Windows-only
- public static string DataMonoBinDir => internal_DataMonoBinDir();
- #endregion
-
-
- #region Internal
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResDataDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResMetadataDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResAssembliesBaseDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResAssembliesDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResConfigDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempAssembliesBaseDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempAssembliesDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoUserDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoLogsDir();
-
- #region Tools-only
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoSolutionsDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_BuildLogsDirs();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ProjectSlnPath();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ProjectCsProjPath();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataEditorToolsDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataEditorPrebuiltApiDir();
- #endregion
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoEtcDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoLibDir();
-
- #region Windows-only
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoBinDir();
- #endregion
-
- #endregion
+ public static string ResMetadataDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string MonoUserDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_MonoUserDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string BuildLogsDirs
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string DataEditorToolsDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static void RegisterProjectSettings()
+ {
+ GlobalDef("dotnet/project/assembly_name", "");
+ GlobalDef("dotnet/project/solution_directory", "");
+ GlobalDef("dotnet/project/c#_project_directory", "");
+ }
+
+ private static void DetermineProjectLocation()
+ {
+ static string DetermineProjectName()
+ {
+ string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ projectAssemblyName = projectAssemblyName.ToSafeDirName();
+ if (string.IsNullOrEmpty(projectAssemblyName))
+ projectAssemblyName = "UnnamedProject";
+ return projectAssemblyName;
+ }
+
+ _projectAssemblyName = (string)ProjectSettings.GetSetting("dotnet/project/assembly_name");
+ if (string.IsNullOrEmpty(_projectAssemblyName))
+ {
+ _projectAssemblyName = DetermineProjectName();
+ ProjectSettings.SetSetting("dotnet/project/assembly_name", _projectAssemblyName);
+ }
+
+ string slnParentDir = (string)ProjectSettings.GetSetting("dotnet/project/solution_directory");
+ if (string.IsNullOrEmpty(slnParentDir))
+ slnParentDir = "res://";
+
+ string csprojParentDir = (string)ProjectSettings.GetSetting("dotnet/project/c#_project_directory");
+ if (string.IsNullOrEmpty(csprojParentDir))
+ csprojParentDir = "res://";
+
+ _projectSlnPath = Path.Combine(ProjectSettings.GlobalizePath(slnParentDir),
+ string.Concat(_projectAssemblyName, ".sln"));
+
+ _projectCsProjPath = Path.Combine(ProjectSettings.GlobalizePath(csprojParentDir),
+ string.Concat(_projectAssemblyName, ".csproj"));
+ }
+
+ private static string _projectAssemblyName;
+ private static string _projectSlnPath;
+ private static string _projectCsProjPath;
+
+ public static string ProjectAssemblyName
+ {
+ get
+ {
+ if (_projectAssemblyName == null)
+ DetermineProjectLocation();
+ return _projectAssemblyName;
+ }
+ }
+
+ public static string ProjectSlnPath
+ {
+ get
+ {
+ if (_projectSlnPath == null)
+ DetermineProjectLocation();
+ return _projectSlnPath;
+ }
+ }
+
+ public static string ProjectCsProjPath
+ {
+ get
+ {
+ if (_projectCsProjPath == null)
+ DetermineProjectLocation();
+ return _projectCsProjPath;
+ }
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 12c90178c9..e3fe1622d0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -1,114 +1,161 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Godot;
+using Godot.NativeInterop;
+using Godot.SourceGenerators.Internal;
using GodotTools.IdeMessaging.Requests;
namespace GodotTools.Internals
{
- public static class Internal
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ [GenerateUnmanagedCallbacks(typeof(InternalUnmanagedCallbacks))]
+ internal static partial class Internal
{
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = ".cs";
- public static string UpdateApiAssembliesFromPrebuilt(string config) =>
- internal_UpdateApiAssembliesFromPrebuilt(config);
+ public static string FullExportTemplatesDir
+ {
+ get
+ {
+ godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
- public static string FullExportTemplatesDir =>
- internal_FullExportTemplatesDir();
+ public static string SimplifyGodotPath(this string path) => Godot.StringExtensions.SimplifyPath(path);
- public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
+ public static bool IsMacOSAppBundleInstalled(string bundleId)
+ {
+ using godot_string bundleIdIn = Marshaling.ConvertStringToNative(bundleId);
+ return godot_icall_Internal_IsMacOSAppBundleInstalled(bundleIdIn);
+ }
- public static bool IsMacOSAppBundleInstalled(string bundleId) => internal_IsMacOSAppBundleInstalled(bundleId);
+ public static bool GodotIs32Bits() => godot_icall_Internal_GodotIs32Bits();
- public static bool GodotIs32Bits() => internal_GodotIs32Bits();
+ public static bool GodotIsRealTDouble() => godot_icall_Internal_GodotIsRealTDouble();
- public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
+ public static void GodotMainIteration() => godot_icall_Internal_GodotMainIteration();
- public static void GodotMainIteration() => internal_GodotMainIteration();
+ public static bool IsAssembliesReloadingNeeded() => godot_icall_Internal_IsAssembliesReloadingNeeded();
- public static ulong GetCoreApiHash() => internal_GetCoreApiHash();
+ public static void ReloadAssemblies(bool softReload) => godot_icall_Internal_ReloadAssemblies(softReload);
- public static ulong GetEditorApiHash() => internal_GetEditorApiHash();
+ public static void EditorDebuggerNodeReloadScripts() => godot_icall_Internal_EditorDebuggerNodeReloadScripts();
- public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded();
+ public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
+ godot_icall_Internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus);
- public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
+ public static void EditorNodeShowScriptScreen() => godot_icall_Internal_EditorNodeShowScriptScreen();
- public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
+ public static void EditorRunPlay() => godot_icall_Internal_EditorRunPlay();
- public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
- internal_ScriptEditorEdit(resource, line, col, grabFocus);
+ public static void EditorRunStop() => godot_icall_Internal_EditorRunStop();
- public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
+ public static void ScriptEditorDebugger_ReloadScripts() =>
+ godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
- public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
+ public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind,
+ string scriptFile)
+ {
+ using godot_string scriptFileIn = Marshaling.ConvertStringToNative(scriptFile);
+ godot_icall_Internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res);
+ using (res)
+ return Marshaling.ConvertNativePackedStringArrayToSystemArray(res);
+ }
- public static void EditorRunPlay() => internal_EditorRunPlay();
+ #region Internal
- public static void EditorRunStop() => internal_EditorRunStop();
+ private static bool initialized = false;
- public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts();
+ // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
+ internal static unsafe void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ if (initialized)
+ throw new InvalidOperationException("Already initialized");
+ initialized = true;
- public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, string scriptFile) =>
- internal_CodeCompletionRequest((int)kind, scriptFile);
+ if (unmanagedCallbacksSize != sizeof(InternalUnmanagedCallbacks))
+ throw new ArgumentException("Unmanaged callbacks size mismatch");
- #region Internal
+ _unmanagedCallbacks = Unsafe.AsRef<InternalUnmanagedCallbacks>((void*)unmanagedCallbacks);
+ }
+
+ private partial struct InternalUnmanagedCallbacks
+ {
+ }
+
+ /*
+ * IMPORTANT:
+ * The order of the methods defined in NativeFuncs must match the order
+ * in the array defined at the bottom of 'editor/editor_internal_calls.cpp'.
+ */
+
+ public static partial void godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_MonoUserDir(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);
+
+ public static partial void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
+ int amount, bool canCancel);
+
+ public static partial void godot_icall_EditorProgress_Dispose(in godot_string task);
+
+ public static partial bool godot_icall_EditorProgress_Step(in godot_string task, in godot_string state,
+ int step,
+ bool forceRefresh);
+
+ private static partial void godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);
+
+ private static partial bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
+ private static partial bool godot_icall_Internal_GodotIs32Bits();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_FullExportTemplatesDir();
+ private static partial bool godot_icall_Internal_GodotIsRealTDouble();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_SimplifyGodotPath(this string path);
+ private static partial void godot_icall_Internal_GodotMainIteration();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_IsMacOSAppBundleInstalled(string bundleId);
+ private static partial bool godot_icall_Internal_IsAssembliesReloadingNeeded();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_GodotIs32Bits();
+ private static partial void godot_icall_Internal_ReloadAssemblies(bool softReload);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_GodotIsRealTDouble();
+ private static partial void godot_icall_Internal_EditorDebuggerNodeReloadScripts();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_GodotMainIteration();
+ private static partial bool godot_icall_Internal_ScriptEditorEdit(IntPtr resource, int line, int col,
+ bool grabFocus);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern ulong internal_GetCoreApiHash();
+ private static partial void godot_icall_Internal_EditorNodeShowScriptScreen();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern ulong internal_GetEditorApiHash();
+ private static partial void godot_icall_Internal_EditorRunPlay();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_IsAssembliesReloadingNeeded();
+ private static partial void godot_icall_Internal_EditorRunStop();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_ReloadAssemblies(bool softReload);
+ private static partial void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorDebuggerNodeReloadScripts();
+ private static partial void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
+ out godot_packed_string_array res);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
+ public static partial float godot_icall_Globals_EditorScale();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorNodeShowScriptScreen();
+ public static partial void godot_icall_Globals_GlobalDef(in godot_string setting, in godot_variant defaultValue,
+ bool restartIfChanged, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoWindowsInstallRoot();
+ public static partial void godot_icall_Globals_EditorDef(in godot_string setting, in godot_variant defaultValue,
+ bool restartIfChanged, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorRunPlay();
+ public static partial void
+ godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorRunStop();
+ public static partial void godot_icall_Globals_TTR(in godot_string text, out godot_string dest);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_ScriptEditorDebugger_ReloadScripts();
+ public static partial void godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string[] internal_CodeCompletionRequest(int kind, string scriptFile);
+ public static partial bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(in godot_string filePath);
#endregion
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
index 05499339b1..89bda704bb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
@@ -1,14 +1,14 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using Godot;
using GodotTools.Core;
-using JetBrains.Annotations;
namespace GodotTools.Utils
{
public static class FsPathUtils
{
- private static readonly string _resourcePath = ProjectSettings.GlobalizePath("res://");
+ private static readonly string ResourcePath = ProjectSettings.GlobalizePath("res://");
private static bool PathStartsWithAlreadyNorm(this string childPath, string parentPath)
{
@@ -30,11 +30,11 @@ namespace GodotTools.Utils
return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
}
- [CanBeNull]
+ [return: MaybeNull]
public static string LocalizePathWithCaseChecked(string path)
{
string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
- string resourcePathNorm = _resourcePath.NormalizePath() + Path.DirectorySeparatorChar;
+ string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm))
return null;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 5cef6e5c3c..651922d019 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -1,24 +1,24 @@
+using Godot.NativeInterop;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
-using System.Runtime.CompilerServices;
-using JetBrains.Annotations;
+using System.Runtime.Versioning;
+using System.Text;
+using GodotTools.Internals;
namespace GodotTools.Utils
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static class OS
{
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string GetPlatformName();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool UnixFileHasExecutableAccess(string filePath);
-
- public static class Names
+ /// <summary>
+ /// Display names for the OS platforms.
+ /// </summary>
+ private static class Names
{
public const string Windows = "Windows";
public const string MacOS = "macOS";
@@ -26,7 +26,6 @@ namespace GodotTools.Utils
public const string FreeBSD = "FreeBSD";
public const string NetBSD = "NetBSD";
public const string BSD = "BSD";
- public const string Server = "Server";
public const string UWP = "UWP";
public const string Haiku = "Haiku";
public const string Android = "Android";
@@ -34,12 +33,14 @@ namespace GodotTools.Utils
public const string HTML5 = "HTML5";
}
+ /// <summary>
+ /// Godot platform identifiers.
+ /// </summary>
public static class Platforms
{
public const string Windows = "windows";
public const string MacOS = "macos";
public const string LinuxBSD = "linuxbsd";
- public const string Server = "server";
public const string UWP = "uwp";
public const string Haiku = "haiku";
public const string Android = "android";
@@ -47,6 +48,38 @@ namespace GodotTools.Utils
public const string HTML5 = "javascript";
}
+ /// <summary>
+ /// OS name part of the .NET runtime identifier (RID).
+ /// See https://docs.microsoft.com/en-us/dotnet/core/rid-catalog.
+ /// </summary>
+ public static class DotNetOS
+ {
+ public const string Win = "win";
+ public const string OSX = "osx";
+ public const string Linux = "linux";
+ public const string Win10 = "win10";
+ public const string Android = "android";
+ public const string iOS = "ios";
+ public const string Browser = "browser";
+ }
+
+ public static readonly Dictionary<string, string> PlatformFeatureMap = new Dictionary<string, string>(
+ // Export `features` may be in lower case
+ StringComparer.InvariantCultureIgnoreCase
+ )
+ {
+ ["Windows"] = Platforms.Windows,
+ ["macOS"] = Platforms.MacOS,
+ ["LinuxBSD"] = Platforms.LinuxBSD,
+ // "X11" for compatibility, temporarily, while we are on an outdated branch
+ ["X11"] = Platforms.LinuxBSD,
+ ["UWP"] = Platforms.UWP,
+ ["Haiku"] = Platforms.Haiku,
+ ["Android"] = Platforms.Android,
+ ["iOS"] = Platforms.iOS,
+ ["HTML5"] = Platforms.HTML5
+ };
+
public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string>
{
[Names.Windows] = Platforms.Windows,
@@ -55,7 +88,6 @@ namespace GodotTools.Utils
[Names.FreeBSD] = Platforms.LinuxBSD,
[Names.NetBSD] = Platforms.LinuxBSD,
[Names.BSD] = Platforms.LinuxBSD,
- [Names.Server] = Platforms.Server,
[Names.UWP] = Platforms.UWP,
[Names.Haiku] = Platforms.Haiku,
[Names.Android] = Platforms.Android,
@@ -63,47 +95,78 @@ namespace GodotTools.Utils
[Names.HTML5] = Platforms.HTML5
};
+ public static readonly Dictionary<string, string> DotNetOSPlatformMap = new Dictionary<string, string>
+ {
+ [Platforms.Windows] = DotNetOS.Win,
+ [Platforms.MacOS] = DotNetOS.OSX,
+ // TODO:
+ // Does .NET 6 support BSD variants? If it does, it may need the name `unix`
+ // instead of `linux` in the runtime identifier. This would be a problem as
+ // Godot has a single export profile for both, named LinuxBSD.
+ [Platforms.LinuxBSD] = DotNetOS.Linux,
+ [Platforms.UWP] = DotNetOS.Win10,
+ [Platforms.Android] = DotNetOS.Android,
+ [Platforms.iOS] = DotNetOS.iOS,
+ [Platforms.HTML5] = DotNetOS.Browser
+ };
+
private static bool IsOS(string name)
{
- return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+ Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
+ using (dest)
+ {
+ string platformName = Marshaling.ConvertStringToManaged(dest);
+ return name.Equals(platformName, StringComparison.OrdinalIgnoreCase);
+ }
}
private static bool IsAnyOS(IEnumerable<string> names)
{
- return names.Any(p => p.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase));
+ Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
+ using (dest)
+ {
+ string platformName = Marshaling.ConvertStringToManaged(dest);
+ return names.Any(p => p.Equals(platformName, StringComparison.OrdinalIgnoreCase));
+ }
}
private static readonly IEnumerable<string> LinuxBSDPlatforms =
new[] { Names.Linux, Names.FreeBSD, Names.NetBSD, Names.BSD };
private static readonly IEnumerable<string> UnixLikePlatforms =
- new[] { Names.MacOS, Names.Server, Names.Haiku, Names.Android, Names.iOS }
+ new[] { Names.MacOS, Names.Haiku, Names.Android, Names.iOS }
.Concat(LinuxBSDPlatforms).ToArray();
- private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows));
- private static readonly Lazy<bool> _isMacOS = new Lazy<bool>(() => IsOS(Names.MacOS));
- private static readonly Lazy<bool> _isLinuxBSD = new Lazy<bool>(() => IsAnyOS(LinuxBSDPlatforms));
- private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server));
- private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP));
- private static readonly Lazy<bool> _isHaiku = new Lazy<bool>(() => IsOS(Names.Haiku));
- private static readonly Lazy<bool> _isAndroid = new Lazy<bool>(() => IsOS(Names.Android));
- private static readonly Lazy<bool> _isiOS = new Lazy<bool>(() => IsOS(Names.iOS));
- private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
- private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms));
-
- public static bool IsWindows => _isWindows.Value || IsUWP;
- public static bool IsMacOS => _isMacOS.Value;
- public static bool IsLinuxBSD => _isLinuxBSD.Value;
- public static bool IsServer => _isServer.Value;
- public static bool IsUWP => _isUWP.Value;
+ private static readonly Lazy<bool> _isWindows = new(() => IsOS(Names.Windows));
+ private static readonly Lazy<bool> _isMacOS = new(() => IsOS(Names.MacOS));
+ private static readonly Lazy<bool> _isLinuxBSD = new(() => IsAnyOS(LinuxBSDPlatforms));
+ private static readonly Lazy<bool> _isUWP = new(() => IsOS(Names.UWP));
+ private static readonly Lazy<bool> _isHaiku = new(() => IsOS(Names.Haiku));
+ private static readonly Lazy<bool> _isAndroid = new(() => IsOS(Names.Android));
+ private static readonly Lazy<bool> _isiOS = new(() => IsOS(Names.iOS));
+ private static readonly Lazy<bool> _isHTML5 = new(() => IsOS(Names.HTML5));
+ private static readonly Lazy<bool> _isUnixLike = new(() => IsAnyOS(UnixLikePlatforms));
+
+ [SupportedOSPlatformGuard("windows")] public static bool IsWindows => _isWindows.Value || IsUWP;
+
+ [SupportedOSPlatformGuard("osx")] public static bool IsMacOS => _isMacOS.Value;
+
+ [SupportedOSPlatformGuard("linux")] public static bool IsLinuxBSD => _isLinuxBSD.Value;
+
+ [SupportedOSPlatformGuard("windows")] public static bool IsUWP => _isUWP.Value;
+
public static bool IsHaiku => _isHaiku.Value;
- public static bool IsAndroid => _isAndroid.Value;
- public static bool IsiOS => _isiOS.Value;
- public static bool IsHTML5 => _isHTML5.Value;
+
+ [SupportedOSPlatformGuard("android")] public static bool IsAndroid => _isAndroid.Value;
+
+ [SupportedOSPlatformGuard("ios")] public static bool IsiOS => _isiOS.Value;
+
+ [SupportedOSPlatformGuard("browser")] public static bool IsHTML5 => _isHTML5.Value;
public static bool IsUnixLike => _isUnixLike.Value;
public static char PathSep => IsWindows ? ';' : ':';
+ [return: MaybeNull]
public static string PathWhich([NotNull] string name)
{
if (IsWindows)
@@ -112,9 +175,11 @@ namespace GodotTools.Utils
return PathWhichUnix(name);
}
+ [return: MaybeNull]
private static string PathWhichWindows([NotNull] string name)
{
- string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
+ string[] windowsExts =
+ Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
char[] invalidPathChars = Path.GetInvalidPathChars();
@@ -133,7 +198,7 @@ namespace GodotTools.Utils
string nameExt = Path.GetExtension(name);
bool hasPathExt = !string.IsNullOrEmpty(nameExt) &&
- windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
+ windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
@@ -141,12 +206,13 @@ namespace GodotTools.Utils
return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists);
return (from dir in searchDirs
- select Path.Combine(dir, name)
+ select Path.Combine(dir, name)
into path
- from ext in windowsExts
- select path + ext).FirstOrDefault(File.Exists);
+ from ext in windowsExts
+ select path + ext).FirstOrDefault(File.Exists);
}
+ [return: MaybeNull]
private static string PathWhichUnix([NotNull] string name)
{
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
@@ -168,19 +234,16 @@ namespace GodotTools.Utils
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
return searchDirs.Select(dir => Path.Combine(dir, name))
- .FirstOrDefault(path => File.Exists(path) && UnixFileHasExecutableAccess(path));
+ .FirstOrDefault(path =>
+ {
+ using godot_string pathIn = Marshaling.ConvertStringToNative(path);
+ return File.Exists(path) && Internal.godot_icall_Utils_OS_UnixFileHasExecutableAccess(pathIn);
+ });
}
public static void RunProcess(string command, IEnumerable<string> arguments)
{
- // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
- string CmdLineArgsToString(IEnumerable<string> args)
- {
- // Not perfect, but as long as we are careful...
- return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
- }
-
- var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
+ var startInfo = new ProcessStartInfo(command)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
@@ -188,44 +251,104 @@ namespace GodotTools.Utils
CreateNoWindow = true
};
- using (Process process = Process.Start(startInfo))
- {
- if (process == null)
- throw new Exception("No process was started");
+ foreach (string arg in arguments)
+ startInfo.ArgumentList.Add(arg);
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
- if (IsWindows && process.Id > 0)
- User32Dll.AllowSetForegroundWindow(process.Id); // allows application to focus itself
- }
+ using Process process = Process.Start(startInfo);
+
+ if (process == null)
+ throw new Exception("No process was started");
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ if (IsWindows && process.Id > 0)
+ User32Dll.AllowSetForegroundWindow(process.Id); // Allows application to focus itself
}
public static int ExecuteCommand(string command, IEnumerable<string> arguments)
{
- // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
- string CmdLineArgsToString(IEnumerable<string> args)
+ var startInfo = new ProcessStartInfo(command)
{
- // Not perfect, but as long as we are careful...
- return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
- }
+ // Print the output
+ RedirectStandardOutput = false,
+ RedirectStandardError = false,
+ UseShellExecute = false
+ };
+
+ foreach (string arg in arguments)
+ startInfo.ArgumentList.Add(arg);
- var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments));
+ Console.WriteLine(startInfo.GetCommandLineDisplay(new StringBuilder("Executing: ")).ToString());
- Console.WriteLine($"Executing: \"{startInfo.FileName}\" {startInfo.Arguments}");
+ using var process = new Process { StartInfo = startInfo };
+ process.Start();
+ process.WaitForExit();
+
+ return process.ExitCode;
+ }
- // Print the output
- startInfo.RedirectStandardOutput = false;
- startInfo.RedirectStandardError = false;
+ private static void AppendProcessFileNameForDisplay(this StringBuilder builder, string fileName)
+ {
+ if (builder.Length > 0)
+ builder.Append(' ');
+
+ if (fileName.Contains(' '))
+ {
+ builder.Append('"');
+ builder.Append(fileName);
+ builder.Append('"');
+ }
+ else
+ {
+ builder.Append(fileName);
+ }
+ }
- startInfo.UseShellExecute = false;
+ private static void AppendProcessArgumentsForDisplay(this StringBuilder builder,
+ Collection<string> argumentList)
+ {
+ // This is intended just for reading. It doesn't need to be a valid command line.
+ // E.g.: We don't handle escaping of quotes.
- using (var process = new Process { StartInfo = startInfo })
+ foreach (string argument in argumentList)
{
- process.Start();
- process.WaitForExit();
+ if (builder.Length > 0)
+ builder.Append(' ');
- return process.ExitCode;
+ if (argument.Contains(' '))
+ {
+ builder.Append('"');
+ builder.Append(argument);
+ builder.Append('"');
+ }
+ else
+ {
+ builder.Append(argument);
+ }
}
}
+
+ public static StringBuilder GetCommandLineDisplay(
+ this ProcessStartInfo startInfo,
+ StringBuilder optionalBuilder = null
+ )
+ {
+ var builder = optionalBuilder ?? new StringBuilder();
+
+ builder.AppendProcessFileNameForDisplay(startInfo.FileName);
+
+ if (startInfo.ArgumentList.Count == 0)
+ {
+ builder.Append(' ');
+ builder.Append(startInfo.Arguments);
+ }
+ else
+ {
+ builder.AppendProcessArgumentsForDisplay(startInfo.ArgumentList);
+ }
+
+ return builder;
+ }
}
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 2e628cb576..73d8f23081 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -41,69 +41,101 @@
#include "core/string/ucaps.h"
#include "main/main.h"
-#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_defs.h"
-#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
+StringBuilder &operator<<(StringBuilder &r_sb, const String &p_string) {
+ r_sb.append(p_string);
+ return r_sb;
+}
+
+StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
+ r_sb.append(p_cstring);
+ return r_sb;
+}
+
#define CS_INDENT " " // 4 whitespaces
#define INDENT1 CS_INDENT
#define INDENT2 INDENT1 INDENT1
#define INDENT3 INDENT2 INDENT1
#define INDENT4 INDENT3 INDENT1
-#define INDENT5 INDENT4 INDENT1
-#define MEMBER_BEGIN "\n" INDENT2
+#define MEMBER_BEGIN "\n" INDENT1
#define OPEN_BLOCK "{\n"
#define CLOSE_BLOCK "}\n"
-#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3
-#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4
+#define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK
+#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK
+#define CLOSE_BLOCK_L1 INDENT1 CLOSE_BLOCK
#define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
#define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
-#define CS_FIELD_MEMORYOWN "memoryOwn"
+#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
+#define BINDINGS_NATIVE_NAME_FIELD "NativeName"
+
+#define CS_PARAM_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
-#define CS_SMETHOD_GETINSTANCE "GetPtr"
+#define CS_STATIC_METHOD_GETINSTANCE "GetPtr"
#define CS_METHOD_CALL "Call"
+#define CS_PROPERTY_SINGLETON "Singleton"
+#define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
+#define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod"
+
+#define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
+#define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
+#define CS_STATIC_FIELD_METHOD_NAME_PREFIX "MethodName_"
+#define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_"
+#define CS_STATIC_FIELD_SIGNAL_NAME_PREFIX "SignalName_"
-#define GLUE_HEADER_FILE "glue_header.h"
#define ICALL_PREFIX "godot_icall_"
-#define SINGLETON_ICALL_SUFFIX "_get_singleton"
-#define ICALL_GET_METHODBIND "__ClassDB_get_method"
+#define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method"
+#define ICALL_CLASSDB_GET_CONSTRUCTOR "ClassDB_get_constructor"
#define C_LOCAL_RET "ret"
#define C_LOCAL_VARARG_RET "vararg_ret"
#define C_LOCAL_PTRCALL_ARGS "call_args"
-#define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT"
-
-#define C_NS_MONOUTILS "GDMonoUtils"
-#define C_NS_MONOINTERNALS "GDMonoInternals"
-#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOINTERNALS "::tie_managed_to_unmanaged"
-#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS "::unmanaged_get_managed"
-
-#define C_NS_MONOMARSHAL "GDMonoMarshal"
-#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL "::mono_object_to_variant"
-#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL "::variant_to_mono_object"
-#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL "::mono_string_to_godot"
-#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
-#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
-#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable"
-#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed"
-#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
-#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
-
-#define BINDINGS_GENERATOR_VERSION UINT32_C(13)
+
+#define C_CLASS_NATIVE_FUNCS "NativeFuncs"
+#define C_NS_MONOUTILS "InteropUtils"
+#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged"
+#define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton"
+
+#define C_NS_MONOMARSHAL "Marshaling"
+#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL ".ConvertStringToNative"
+#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL ".ConvertStringToManaged"
+#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL ".ConvertSystemArrayToNative" #m_type
+#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL ".ConvertNative" #m_type "ToSystemArray"
+#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToNative"
+#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToManaged"
+#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToNative"
+#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged"
// Types that will be ignored by the generator and won't be available in C#.
const Vector<String> ignored_types = { "PhysicsServer3DExtension" };
-const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
+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_expr = "(int)%0";
+ 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.
@@ -359,23 +391,23 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append(tag);
xml_output.append("</c>");
} else if (tag == "PackedByteArray") {
- xml_output.append("<see cref=\"T:byte[]\"/>");
+ xml_output.append("<see cref=\"byte\"/>[]");
} else if (tag == "PackedInt32Array") {
- xml_output.append("<see cref=\"T:int[]\"/>");
+ xml_output.append("<see cref=\"int\"/>[]");
} else if (tag == "PackedInt64Array") {
- xml_output.append("<see cref=\"T:long[]\"/>");
+ xml_output.append("<see cref=\"long\"/>[]");
} else if (tag == "PackedFloat32Array") {
- xml_output.append("<see cref=\"T:float[]\"/>");
+ xml_output.append("<see cref=\"float\"/>[]");
} else if (tag == "PackedFloat64Array") {
- xml_output.append("<see cref=\"T:double[]\"/>");
+ xml_output.append("<see cref=\"double\"/>[]");
} else if (tag == "PackedStringArray") {
- xml_output.append("<see cref=\"T:string[]\"/>");
+ xml_output.append("<see cref=\"string\"/>[]");
} else if (tag == "PackedVector2Array") {
- xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector2[]\"/>");
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>[]");
} else if (tag == "PackedVector3Array") {
- xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector3[]\"/>");
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>[]");
} else if (tag == "PackedColorArray") {
- xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Color[]\"/>");
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>[]");
} else {
const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
@@ -794,49 +826,28 @@ 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";
-
- i++;
+ im_unique_sig += get_arg_unique_sig(*arg_type);
}
// godot_icall_{argc}_{icallcount}
@@ -845,7 +856,15 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
icall_method += "_";
icall_method += itos(method_icalls.size());
- InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig);
+ InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_unique_sig);
+
+ im_icall.is_vararg = imethod.is_vararg;
+ im_icall.is_static = imethod.is_static;
+ im_icall.return_type = imethod.return_type;
+
+ for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
+ im_icall.argument_types.push_back(F->get().type);
+ }
List<InternalCall>::Element *match = method_icalls.find(im_icall);
@@ -859,47 +878,49 @@ 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) {
+ p_output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
p_output.append("using System;\n\n");
- p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
// The class where we put the extensions doesn't matter, so just use "GD".
- p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
+ p_output.append("public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n{");
#define ARRAY_IS_EMPTY(m_type) \
- p_output.append("\n" INDENT2 "/// <summary>\n"); \
- p_output.append(INDENT2 "/// Returns true if this " #m_type " array is empty or doesn't exist.\n"); \
- p_output.append(INDENT2 "/// </summary>\n"); \
- p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \
- p_output.append(INDENT2 "/// <returns>Whether or not the array is empty.</returns>\n"); \
- p_output.append(INDENT2 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
- p_output.append(INDENT3 "return instance == null || instance.Length == 0;\n"); \
- p_output.append(INDENT2 CLOSE_BLOCK);
+ p_output.append("\n" INDENT1 "/// <summary>\n"); \
+ p_output.append(INDENT1 "/// Returns true if this " #m_type " array is empty or doesn't exist.\n"); \
+ p_output.append(INDENT1 "/// </summary>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \
+ p_output.append(INDENT1 "/// <returns>Whether or not the array is empty.</returns>\n"); \
+ p_output.append(INDENT1 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \
+ p_output.append(OPEN_BLOCK_L1); \
+ p_output.append(INDENT2 "return instance == null || instance.Length == 0;\n"); \
+ p_output.append(INDENT1 CLOSE_BLOCK);
#define ARRAY_JOIN(m_type) \
- p_output.append("\n" INDENT2 "/// <summary>\n"); \
- p_output.append(INDENT2 "/// Converts this " #m_type " array to a string delimited by the given string.\n"); \
- p_output.append(INDENT2 "/// </summary>\n"); \
- p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
- p_output.append(INDENT2 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \
- p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
- p_output.append(INDENT2 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
- p_output.append(INDENT3 "return String.Join(delimiter, instance);\n"); \
- p_output.append(INDENT2 CLOSE_BLOCK);
+ p_output.append("\n" INDENT1 "/// <summary>\n"); \
+ p_output.append(INDENT1 "/// Converts this " #m_type " array to a string delimited by the given string.\n"); \
+ p_output.append(INDENT1 "/// </summary>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \
+ p_output.append(INDENT1 "/// <returns>A single string with all items.</returns>\n"); \
+ p_output.append(INDENT1 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \
+ p_output.append(OPEN_BLOCK_L1); \
+ p_output.append(INDENT2 "return String.Join(delimiter, instance);\n"); \
+ p_output.append(INDENT1 CLOSE_BLOCK);
#define ARRAY_STRINGIFY(m_type) \
- p_output.append("\n" INDENT2 "/// <summary>\n"); \
- p_output.append(INDENT2 "/// Converts this " #m_type " array to a string with brackets.\n"); \
- p_output.append(INDENT2 "/// </summary>\n"); \
- p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
- p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
- p_output.append(INDENT2 "public static string Stringify(this " #m_type "[] instance)\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
- p_output.append(INDENT3 "return \"[\" + instance.Join() + \"]\";\n"); \
- p_output.append(INDENT2 CLOSE_BLOCK);
+ p_output.append("\n" INDENT1 "/// <summary>\n"); \
+ p_output.append(INDENT1 "/// Converts this " #m_type " array to a string with brackets.\n"); \
+ p_output.append(INDENT1 "/// </summary>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
+ p_output.append(INDENT1 "/// <returns>A single string with all items.</returns>\n"); \
+ p_output.append(INDENT1 "public static string Stringify(this " #m_type "[] instance)\n"); \
+ p_output.append(OPEN_BLOCK_L1); \
+ p_output.append(INDENT2 "return \"[\" + instance.Join() + \"]\";\n"); \
+ p_output.append(INDENT1 CLOSE_BLOCK);
#define ARRAY_ALL(m_type) \
ARRAY_IS_EMPTY(m_type) \
@@ -925,18 +946,18 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
#undef ARRAY_JOIN
#undef ARRAY_STRINGIFY
- p_output.append(INDENT1 CLOSE_BLOCK); // End of GD class.
- p_output.append(CLOSE_BLOCK); // End of namespace.
+ p_output.append(CLOSE_BLOCK); // End of GD class.
}
void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
// Constants (in partial GD class)
+ p_output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
+
p_output.append("\n#pragma warning disable CS1591 // Disable warning: "
"'Missing XML comment for publicly visible type or member'\n");
- p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
+ p_output.append("public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n{");
for (const ConstantInterface &iconstant : global_constants) {
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
@@ -947,12 +968,12 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
@@ -967,7 +988,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("\n");
}
- p_output.append(INDENT1 CLOSE_BLOCK); // end of GD class
+ p_output.append(CLOSE_BLOCK); // end of GD class
// Enums
@@ -985,21 +1006,21 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
CRASH_COND(enum_class_name != "Variant"); // Hard-coded...
- _log("Declaring global enum '%s' inside static class '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
+ _log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
- p_output.append("\n" INDENT1 "public static partial class ");
+ p_output.append("\npublic partial struct ");
p_output.append(enum_class_name);
- p_output.append("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK);
}
if (ienum.is_flags) {
- p_output.append("\n" INDENT1 "[System.Flags]");
+ p_output.append("\n[System.Flags]");
}
- p_output.append("\n" INDENT1 "public enum ");
+ p_output.append("\npublic enum ");
p_output.append(enum_proxy_name);
p_output.append(" : long");
- p_output.append("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK);
const ConstantInterface &last = ienum.constants.back()->get();
for (const ConstantInterface &iconstant : ienum.constants) {
@@ -1008,34 +1029,32 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
- p_output.append(INDENT2 "/// <summary>\n");
+ p_output.append(INDENT1 "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>\n");
+ p_output.append(INDENT1 "/// </summary>\n");
}
}
- p_output.append(INDENT2);
+ p_output.append(INDENT1);
p_output.append(iconstant.proxy_name);
p_output.append(" = ");
p_output.append(itos(iconstant.value));
p_output.append(&iconstant != &last ? ",\n" : "\n");
}
- p_output.append(INDENT1 CLOSE_BLOCK);
+ p_output.append(CLOSE_BLOCK);
if (enum_in_static_class) {
- p_output.append(INDENT1 CLOSE_BLOCK);
+ p_output.append(CLOSE_BLOCK);
}
}
- p_output.append(CLOSE_BLOCK); // end of namespace
-
p_output.append("\n#pragma warning restore CS1591\n");
}
@@ -1106,42 +1125,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("namespace " BINDINGS_NAMESPACE ";\n\n");
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 "\n" INDENT1 "{");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
+ cs_icalls_content.append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ cs_icalls_content.append("internal static class " BINDINGS_CLASS_NATIVECALLS "\n{");
cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
- cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
- cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = ");
- cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = ");
- cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_CORE)) + ";\n");
-#define ADD_INTERNAL_CALL(m_icall) \
- if (!m_icall.editor_only) { \
- cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal static extern "); \
- cs_icalls_content.append(m_icall.im_type_out + " "); \
- cs_icalls_content.append(m_icall.name + "("); \
- cs_icalls_content.append(m_icall.im_sig + ");\n"); \
- }
+ cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
- for (const InternalCall &internal_call : core_custom_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
- for (const InternalCall &internal_call : method_icalls) {
- ADD_INTERNAL_CALL(internal_call);
+ for (const InternalCall &icall : method_icalls) {
+ if (icall.editor_only) {
+ continue;
+ }
+ Error err = _generate_cs_native_calls(icall, cs_icalls_content);
+ if (err != OK) {
+ return err;
+ }
}
-#undef ADD_INTERNAL_CALL
-
- cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ cs_icalls_content.append(CLOSE_BLOCK);
String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
@@ -1152,6 +1167,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(internal_methods_file);
+ // Generate GeneratedIncludes.props
+
StringBuilder includes_props_content;
includes_props_content.append("<Project>\n"
" <ItemGroup>\n");
@@ -1215,41 +1232,40 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
+ // Generate native calls
+
StringBuilder cs_icalls_content;
+ cs_icalls_content.append("namespace " BINDINGS_NAMESPACE ";\n\n");
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(INDENT2 "internal static ulong godot_api_hash = ");
- cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
- cs_icalls_content.append(INDENT2 "internal static uint bindings_version = ");
- cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.append(INDENT2 "internal static uint cs_glue_version = ");
- cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
- cs_icalls_content.append("\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
+ cs_icalls_content.append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ cs_icalls_content.append("internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" OPEN_BLOCK);
-#define ADD_INTERNAL_CALL(m_icall) \
- if (m_icall.editor_only) { \
- cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal static extern "); \
- cs_icalls_content.append(m_icall.im_type_out + " "); \
- cs_icalls_content.append(m_icall.name + "("); \
- cs_icalls_content.append(m_icall.im_sig + ");\n"); \
- }
+ cs_icalls_content.append(INDENT1 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_EDITOR)) + ";\n");
- for (const InternalCall &internal_call : editor_custom_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
- for (const InternalCall &internal_call : method_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
+ cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
-#undef ADD_INTERNAL_CALL
+ cs_icalls_content.append("\n");
- cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ for (const InternalCall &icall : method_icalls) {
+ if (!icall.editor_only) {
+ continue;
+ }
+ Error err = _generate_cs_native_calls(icall, cs_icalls_content);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ cs_icalls_content.append(CLOSE_BLOCK);
String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
@@ -1260,6 +1276,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
compile_items.push_back(internal_methods_file);
+ // Generate GeneratedIncludes.props
+
StringBuilder includes_props_content;
includes_props_content.append("<Project>\n"
" <ItemGroup>\n");
@@ -1343,16 +1361,15 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
CRASH_COND(itype.is_singleton);
}
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
-
_log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
-
StringBuilder output;
+ output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
+
output.append("using System;\n"); // IntPtr
output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
+ output.append("using Godot.NativeInterop;\n");
output.append("\n"
"#pragma warning disable CS1591 // Disable warning: "
@@ -1360,7 +1377,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"#pragma warning disable CS1573 // Disable warning: "
"'Parameter has no matching param tag in the XML comment'\n");
- output.append("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ output.append("\n#nullable disable\n");
const DocData::ClassDoc *class_doc = itype.class_doc;
@@ -1369,40 +1386,48 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
- output.append(INDENT1 "/// <summary>\n");
+ output.append("/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT1 "/// ");
+ output.append("/// ");
output.append(summary_lines[i]);
output.append("\n");
}
- output.append(INDENT1 "/// </summary>\n");
+ output.append("/// </summary>\n");
}
}
- output.append(INDENT1 "public ");
+ // We generate a `GodotClassName` attribute if the engine class name is not the same as the
+ // generated C# class name. This allows introspection code to find the name associated with
+ // the class. If the attribute is not present, the C# class name can be used instead.
+ if (itype.name != itype.proxy_name) {
+ output << "[GodotClassName(\"" << itype.name << "\")]\n";
+ }
+
+ output.append("public ");
if (itype.is_singleton) {
output.append("static partial class ");
} else {
- output.append(itype.is_instantiable ? "partial class " : "abstract partial class ");
+ // Even if the class is not instantiable, we can't declare it abstract because
+ // the engine can still instantiate them and return them via the scripting API.
+ // Example: `SceneTreeTimer` returned from `SceneTree.create_timer`.
+ // See the reverted commit: ef5672d3f94a7321ed779c922088bb72adbb1521
+ output.append("partial class ");
}
output.append(itype.proxy_name);
- if (itype.is_singleton) {
- output.append("\n");
- } else if (is_derived_type) {
+ if (is_derived_type && !itype.is_singleton) {
if (obj_types.has(itype.base_name)) {
output.append(" : ");
output.append(obj_types[itype.base_name].proxy_name);
- output.append("\n");
} else {
ERR_PRINT("Base type '" + itype.base_name.operator String() + "' does not exist, for class '" + itype.name + "'.");
return ERR_INVALID_DATA;
}
}
- output.append(INDENT1 "{");
+ output.append("\n{");
// Add constants
@@ -1415,12 +1440,12 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT2 "/// ");
+ output.append(INDENT1 "/// ");
output.append(summary_lines[i]);
output.append("\n");
}
- output.append(INDENT2 "/// </summary>");
+ output.append(INDENT1 "/// </summary>");
}
}
@@ -1456,26 +1481,26 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
- output.append(INDENT3 "/// <summary>\n");
+ output.append(INDENT2 "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT3 "/// ");
+ output.append(INDENT2 "/// ");
output.append(summary_lines[i]);
output.append("\n");
}
- output.append(INDENT3 "/// </summary>\n");
+ output.append(INDENT2 "/// </summary>\n");
}
}
- output.append(INDENT3);
+ output.append(INDENT2);
output.append(iconstant.proxy_name);
output.append(" = ");
output.append(itos(iconstant.value));
output.append(&iconstant != &last ? ",\n" : "\n");
}
- output.append(INDENT2 CLOSE_BLOCK);
+ output.append(INDENT1 CLOSE_BLOCK);
}
// Add properties
@@ -1491,55 +1516,68 @@ 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" INDENT1 "{\n"
+ << INDENT2 "get\n" INDENT2 "{\n" INDENT3 "if (singleton == null)\n"
+ << INDENT4 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(typeof("
+ << itype.proxy_name
+ << ").Name);\n" INDENT3 "return singleton;\n" INDENT2 "}\n" INDENT1 "}\n";
+
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
+ } else {
+ // IMPORTANT: We also generate the static fields for Godot.Object instead of declaring
+ // them manually in the `Object.base.cs` partial class declaration, because they're
+ // required by other static fields in this generated partial class declaration.
+ // Static fields are initialized in order of declaration, but when they're in different
+ // partial class declarations then it becomes harder to tell (Rider warns about this).
- output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.append("." ICALL_PREFIX);
- output.append(itype.name);
- output.append(SINGLETON_ICALL_SUFFIX "();\n");
- } else if (is_derived_type) {
- // Add member fields
+ // Add native name static field
- output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ if (is_derived_type) {
+ output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n";
+ }
+
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
- // Add default constructor
if (itype.is_instantiable) {
- output.append(MEMBER_BEGIN "public ");
- output.append(itype.proxy_name);
- output.append("() : this(");
- output.append(itype.memory_own ? "true" : "false");
-
- // The default constructor may also be called by the engine when instancing existing native objects
- // The engine will initialize the pointer field of the managed side before calling the constructor
- // This is why we only allocate a new native object from the constructor if the pointer field is not set
- output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.append("." + ctor_method);
- output.append("(this);\n" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2);
- } else {
- // Hide the constructor
+ // Add native constructor static field
+
+ output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT1 "private static readonly unsafe delegate* unmanaged<IntPtr> "
+ << CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR
+ << "(" BINDINGS_NATIVE_NAME_FIELD ");\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_L1
+ << INDENT2 "unsafe\n" INDENT2 OPEN_BLOCK
+ << INDENT3 "_ConstructAndInitialize(" CS_STATIC_FIELD_NATIVE_CTOR ", "
+ << BINDINGS_NATIVE_NAME_FIELD ", CachedType, refCounted: "
+ << (itype.is_ref_counted ? "true" : "false") << ");\n"
+ << CLOSE_BLOCK_L2 CLOSE_BLOCK_L1;
+ } else {
+ // Hide the constructor
+ output.append(MEMBER_BEGIN "internal ");
+ output.append(itype.proxy_name);
+ output.append("() {}\n");
+ }
+
+ // Add.. em.. trick constructor. Sort of.
output.append(MEMBER_BEGIN "internal ");
output.append(itype.proxy_name);
- output.append("() {}\n");
+ output.append("(bool " CS_PARAM_MEMORYOWN ") : base(" CS_PARAM_MEMORYOWN ") {}\n");
}
-
- // Add.. em.. trick constructor. Sort of.
- output.append(MEMBER_BEGIN "internal ");
- output.append(itype.proxy_name);
- output.append("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
}
+ // Methods
+
int method_bind_count = 0;
for (const MethodInterface &imethod : itype.methods) {
Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
@@ -1547,30 +1585,153 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
}
+ // Signals
+
for (const SignalInterface &isignal : itype.signals_) {
Error method_err = _generate_cs_signal(itype, isignal, output);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"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");
+ // Script members look-up
- if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
- custom_icalls.push_back(singleton_icall);
+ if (!itype.is_singleton && (is_derived_type || itype.has_virtual_methods)) {
+ // Generate method names cache fields
+
+ for (const MethodInterface &imethod : itype.methods) {
+ if (!imethod.is_virtual) {
+ continue;
+ }
+
+ output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
+ << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT1 "private static readonly StringName "
+ << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name
+ << " = \"" << imethod.name << "\";\n";
+
+ output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
+ << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT1 "private static readonly StringName "
+ << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << " = \"" << imethod.proxy_name << "\";\n";
}
- }
- if (is_derived_type && itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
+ // TODO: Only generate HasGodotClassMethod and InvokeGodotClassMethod if there's any method
+
+ // Generate InvokeGodotClassMethod
+
+ output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
+ << " bool " CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(in godot_string_name method, "
+ << "NativeVariantPtrArgs args, int argCount, out godot_variant ret)\n"
+ << INDENT1 "{\n";
+
+ for (const MethodInterface &imethod : itype.methods) {
+ if (!imethod.is_virtual) {
+ continue;
+ }
+
+ // We also call HasGodotClassMethod to ensure the method is overridden and avoid calling
+ // the stub implementation. This solution adds some extra overhead to calls, but it's
+ // much simpler than other solutions. This won't be a problem once we move to function
+ // pointers of generated wrappers for each method, as lookup will only happen once.
+
+ // We check both native names (snake_case) and proxy names (PascalCase)
+ output << INDENT2 "if ((method == " << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << " || method == " << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name
+ << ") && argCount == " << itos(imethod.arguments.size())
+ << " && " << CS_METHOD_HAS_GODOT_CLASS_METHOD << "((godot_string_name)"
+ << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name << ".NativeValue))\n"
+ << INDENT2 "{\n";
+
+ if (imethod.return_type.cname != name_cache.type_void) {
+ output << INDENT3 "var callRet = ";
+ } else {
+ output << INDENT3;
+ }
+
+ output << imethod.proxy_name << "(";
+
+ for (int i = 0; i < imethod.arguments.size(); i++) {
+ const ArgumentInterface &iarg = imethod.arguments[i];
+
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
+
+ if (i != 0) {
+ output << ", ";
+ }
+
+ if (arg_type->cname == name_cache.type_Array_generic || arg_type->cname == name_cache.type_Dictionary_generic) {
+ String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
+
+ output << "new " << arg_cs_type << "(" << sformat(arg_type->cs_variant_to_managed, "args[" + itos(i) + "]", arg_type->cs_type, arg_type->name) << ")";
+ } else {
+ output << sformat(arg_type->cs_variant_to_managed,
+ "args[" + itos(i) + "]", arg_type->cs_type, arg_type->name);
+ }
+ }
+
+ output << ");\n";
+
+ if (imethod.return_type.cname != name_cache.type_void) {
+ const TypeInterface *return_type = _get_type_or_null(imethod.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
+
+ output << INDENT3 "ret = "
+ << sformat(return_type->cs_managed_to_variant, "callRet", return_type->cs_type, return_type->name)
+ << ";\n"
+ << INDENT3 "return true;\n";
+ } else {
+ output << INDENT3 "ret = default;\n"
+ << INDENT3 "return true;\n";
+ }
+
+ output << INDENT2 "}\n";
+ }
+
+ if (is_derived_type) {
+ output << INDENT2 "return base." CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(method, args, argCount, out ret);\n";
+ } else {
+ output << INDENT2 "ret = default;\n"
+ << INDENT2 "return false;\n";
+ }
+
+ output << INDENT1 "}\n";
+
+ // Generate HasGodotClassMethod
+
+ output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
+ << " bool " CS_METHOD_HAS_GODOT_CLASS_METHOD "(in godot_string_name method)\n"
+ << INDENT1 "{\n";
+
+ for (const MethodInterface &imethod : itype.methods) {
+ if (!imethod.is_virtual) {
+ continue;
+ }
- if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
- custom_icalls.push_back(ctor_icall);
+ // We check for native names (snake_case). If we detect one, we call HasGodotClassMethod
+ // again, but this time with the respective proxy name (PascalCase). It's the job of
+ // user derived classes to override the method and check for those. Our C# source
+ // generators take care of generating those override methods.
+ output << INDENT2 "if (method == " << CS_STATIC_FIELD_METHOD_NAME_PREFIX << imethod.name
+ << ")\n" INDENT2 "{\n"
+ << INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_METHOD "("
+ << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << ".NativeValue.DangerousSelfRef))\n" INDENT3 "{\n"
+ << INDENT4 "return true;\n"
+ << INDENT3 "}\n" INDENT2 "}\n";
}
+
+ if (is_derived_type) {
+ output << INDENT2 "return base." CS_METHOD_HAS_GODOT_CLASS_METHOD "(method);\n";
+ } else {
+ output << INDENT2 "return false;\n";
+ }
+
+ output << INDENT1 "}\n";
}
- output.append(INDENT1 CLOSE_BLOCK /* class */
- CLOSE_BLOCK /* namespace */);
+ output.append(CLOSE_BLOCK /* class */);
output.append("\n"
"#pragma warning restore CS1591\n"
@@ -1649,12 +1810,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
@@ -1669,15 +1830,15 @@ 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_L1);
if (getter) {
- p_output.append(INDENT3 "get\n"
+ p_output.append(INDENT2 "get\n"
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
"#pragma warning disable CS0618 // Disable warning about obsolete method\n"
- OPEN_BLOCK_L3);
+ OPEN_BLOCK_L2 INDENT3);
p_output.append("return ");
p_output.append(getter->proxy_name + "(");
@@ -1694,19 +1855,19 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
}
p_output.append(");\n"
- CLOSE_BLOCK_L3
+ CLOSE_BLOCK_L2
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
"#pragma warning restore CS0618\n");
}
if (setter) {
- p_output.append(INDENT3 "set\n"
+ p_output.append(INDENT2 "set\n"
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
"#pragma warning disable CS0618 // Disable warning about obsolete method\n"
- OPEN_BLOCK_L3);
+ OPEN_BLOCK_L2 INDENT3);
p_output.append(setter->proxy_name + "(");
if (p_iprop.index != -1) {
@@ -1722,19 +1883,20 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
}
p_output.append("value);\n"
- CLOSE_BLOCK_L3
+ CLOSE_BLOCK_L2
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
"#pragma warning restore CS0618\n");
}
- p_output.append(CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L1);
return OK;
}
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,14 +1907,21 @@ 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_expr_is_unsafe = false;
String icall_params = method_bind_field;
+
if (!p_imethod.is_static) {
- icall_params += ", " + sformat(p_itype.cs_in, "this");
+ if (p_itype.cs_in.size()) {
+ cs_in_statements << sformat(p_itype.cs_in, p_itype.c_type, "this",
+ String(), String(), String(), INDENT2);
+ }
+
+ icall_params += ", " + sformat(p_itype.cs_in_expr, "this");
}
StringBuilder default_args_doc;
@@ -1760,7 +1929,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 + "'.");
@@ -1813,27 +1983,23 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
// The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
// Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
- String arg_in = iarg.name;
- arg_in += "_in";
+ String arg_or_defval_local = iarg.name;
+ arg_or_defval_local += "OrDefVal";
- 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 << INDENT2 << arg_cs_type << " " << arg_or_defval_local << " = " << 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,10 +2009,18 @@ 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";
+
+ if (arg_type->cs_in.size()) {
+ cs_in_statements << sformat(arg_type->cs_in, arg_type->c_type, arg_or_defval_local,
+ String(), String(), String(), INDENT2);
+ }
- icall_params += arg_type->cs_in.is_empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
+ if (arg_type->cs_in_expr.is_empty()) {
+ icall_params += arg_or_defval_local;
+ } else {
+ icall_params += sformat(arg_type->cs_in_expr, arg_or_defval_local, arg_type->c_type);
+ }
// Apparently the name attribute must not include the @
String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name;
@@ -1855,18 +2029,32 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is <c>" + param_def_arg + "</c>.</param>");
} else {
- icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
+ if (arg_type->cs_in.size()) {
+ cs_in_statements << sformat(arg_type->cs_in, arg_type->c_type, iarg.name,
+ String(), String(), String(), INDENT2);
+ }
+
+ icall_params += arg_type->cs_in_expr.is_empty() ? iarg.name : sformat(arg_type->cs_in_expr, iarg.name, arg_type->c_type);
}
+
+ cs_in_expr_is_unsafe |= arg_type->cs_in_expr_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)]\n"
+ << INDENT1 "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()) {
@@ -1877,12 +2065,12 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
@@ -1890,16 +2078,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(default_args_doc.as_string());
}
- if (!p_imethod.is_internal) {
- // TODO: This alone adds ~0.2 MB of bloat to the core API assembly. It would be
- // better to generate a table in the C++ glue instead. That way the strings wouldn't
- // add that much extra bloat as they're already used in engine code. Also, it would
- // probably be much faster than looking up the attributes when fetching methods.
- p_output.append(MEMBER_BEGIN "[GodotMethod(\"");
- p_output.append(p_imethod.name);
- p_output.append("\")]");
- }
-
if (p_imethod.is_deprecated) {
if (p_imethod.deprecation_message.is_empty()) {
WARN_PRINT("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'.");
@@ -1919,21 +2097,23 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("virtual ");
}
+ if (cs_in_expr_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 + " ");
p_output.append(p_imethod.proxy_name + "(");
- p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2);
+ p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L1);
if (p_imethod.is_virtual) {
// Godot virtual method must be overridden, therefore we return a default value by default.
if (return_type->cname == name_cache.type_void) {
- p_output.append("return;\n" CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L1);
} else {
- p_output.append("return default(");
- p_output.append(return_cs_type);
- p_output.append(");\n" CLOSE_BLOCK_L2);
+ p_output.append(INDENT2 "return default;\n" CLOSE_BLOCK_L1);
}
return OK; // Won't increment method bind count
@@ -1942,7 +2122,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(INDENT2 CS_METHOD_CALL "(\"");
p_output.append(p_imethod.name);
p_output.append("\"");
@@ -1951,7 +2131,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(iarg.name);
}
- p_output.append(");\n" CLOSE_BLOCK_L2);
+ p_output.append(");\n" CLOSE_BLOCK_L1);
return OK; // Won't increment method bind count
}
@@ -1965,20 +2145,21 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
im_call += ".";
im_call += im_icall->name;
- if (p_imethod.arguments.size()) {
- p_output.append(cs_in_statements);
+ if (p_imethod.arguments.size() && cs_in_statements.get_string_length() > 0) {
+ 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 << INDENT2 << im_call << "(" << icall_params << ");\n";
} else if (return_type->cs_out.is_empty()) {
- p_output.append("return " + im_call + "(" + icall_params + ");\n");
+ p_output << INDENT2 "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(), INDENT2));
p_output.append("\n");
}
- p_output.append(CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L1);
}
p_method_bind_count++;
@@ -1992,7 +2173,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 + "'.");
@@ -2024,12 +2206,12 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
@@ -2058,7 +2240,9 @@ 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 "// ReSharper disable once InconsistentNaming\n");
+ p_output.append(INDENT1 "[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,413 +2259,241 @@ 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_L1 INDENT2);
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(INDENT2 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
} else {
- p_output.append(INDENT3 "remove => Disconnect(__signal_name_");
+ p_output.append(INDENT2 "remove => Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
}
p_output.append(p_isignal.name);
p_output.append(", new Callable(value));\n");
- p_output.append(CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L1);
}
return OK;
}
-Error BindingsGenerator::generate_glue(const String &p_output_dir) {
- ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
-
- bool dir_exists = DirAccess::exists(p_output_dir);
- ERR_FAIL_COND_V_MSG(!dir_exists, ERR_FILE_BAD_PATH, "The output directory does not exist.");
-
- StringBuilder output;
-
- output.append("/* THIS FILE IS GENERATED DO NOT EDIT */\n");
- output.append("#include \"" GLUE_HEADER_FILE "\"\n");
- output.append("\n#ifdef MONO_GLUE_ENABLED\n");
+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;
- generated_icall_funcs.clear();
+ const TypeInterface *return_type = _get_type_or_null(p_icall.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
- for (const KeyValue<StringName, TypeInterface> &type_elem : obj_types) {
- const TypeInterface &itype = type_elem.value;
+ StringBuilder c_func_sig;
+ StringBuilder c_in_statements;
+ StringBuilder c_args_var_content;
- bool is_derived_type = itype.base_name != StringName();
+ c_func_sig << "IntPtr " CS_PARAM_METHODBIND;
- if (!is_derived_type) {
- // Some Object assertions
- CRASH_COND(itype.cname != name_cache.type_Object);
- CRASH_COND(!itype.is_instantiable);
- CRASH_COND(itype.api_type != ClassDB::API_CORE);
- CRASH_COND(itype.is_singleton);
- }
-
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
-
- OS::get_singleton()->print("Generating %s...\n", itype.name.utf8().get_data());
-
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
-
- for (const MethodInterface &imethod : itype.methods) {
- Error method_err = _generate_glue_method(itype, imethod, output);
- ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
- "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
- }
-
- if (itype.is_singleton) {
- String singleton_icall_name = ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX;
- InternalCall singleton_icall = InternalCall(itype.api_type, singleton_icall_name, "IntPtr");
-
- if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
- custom_icalls.push_back(singleton_icall);
- }
-
- output.append("Object* ");
- output.append(singleton_icall_name);
- output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\"");
- output.append(itype.proxy_name);
- output.append("\");\n" CLOSE_BLOCK "\n");
- }
-
- if (is_derived_type && itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
-
- if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
- custom_icalls.push_back(ctor_icall);
- }
-
- output.append("Object* ");
- output.append(ctor_method);
- output.append("(MonoObject* obj) " OPEN_BLOCK
- "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
- output.append(itype.name);
- output.append("\");\n"
- "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
- "\treturn instance;\n" CLOSE_BLOCK "\n");
- }
- }
-
- output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n");
-
- output.append("uint64_t get_core_api_hash() { return ");
- output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n");
-
- output.append("#ifdef TOOLS_ENABLED\n"
- "uint64_t get_editor_api_hash() { return ");
- output.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n");
- output.append("#endif // TOOLS_ENABLED\n");
-
- output.append("uint32_t get_bindings_version() { return ");
- output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
-
- output.append("uint32_t get_cs_glue_version() { return ");
- output.append(String::num_uint64(CS_GLUE_VERSION) + "; }\n");
-
- output.append("\nvoid register_generated_icalls() " OPEN_BLOCK);
- output.append("\tgodot_register_glue_header_icalls();\n");
-
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- output.append("\tGDMonoUtils::add_internal_call("); \
- output.append("\"" BINDINGS_NAMESPACE "."); \
- output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
- output.append("::"); \
- output.append(m_icall.name); \
- output.append("\", "); \
- output.append(m_icall.name); \
- output.append(");\n"); \
- }
-
- bool tools_sequence = false;
- for (const InternalCall &internal_call : core_custom_icalls) {
- if (tools_sequence) {
- if (!internal_call.editor_only) {
- tools_sequence = false;
- output.append("#endif\n");
- }
- } else {
- if (internal_call.editor_only) {
- output.append("#ifdef TOOLS_ENABLED\n");
- tools_sequence = true;
- }
- }
-
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
+ if (!p_icall.is_static) {
+ c_func_sig += ", IntPtr " CS_PARAM_INSTANCE;
}
- if (tools_sequence) {
- tools_sequence = false;
- output.append("#endif\n");
- }
-
- output.append("#ifdef TOOLS_ENABLED\n");
- for (const InternalCall &internal_call : editor_custom_icalls) {
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
- output.append("#endif // TOOLS_ENABLED\n");
-
- for (const InternalCall &internal_call : method_icalls) {
- if (tools_sequence) {
- if (!internal_call.editor_only) {
- tools_sequence = false;
- output.append("#endif\n");
- }
- } else {
- if (internal_call.editor_only) {
- output.append("#ifdef TOOLS_ENABLED\n");
- tools_sequence = true;
- }
- }
-
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
-
- if (tools_sequence) {
- tools_sequence = false;
- output.append("#endif\n");
- }
-
-#undef ADD_INTERNAL_CALL_REGISTRATION
-
- output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n");
-
- output.append("\n#endif // MONO_GLUE_ENABLED\n");
-
- Error save_err = _save_file(path::join(p_output_dir, "mono_glue.gen.cpp"), output);
- if (save_err != OK) {
- return save_err;
- }
-
- OS::get_singleton()->print("Mono glue generated successfully\n");
-
- return OK;
-}
-
-uint32_t BindingsGenerator::get_version() {
- return BINDINGS_GENERATOR_VERSION;
-}
-
-Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
- Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
-
- file->store_string(p_content.as_string());
-
- return OK;
-}
-
-Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) {
- if (p_imethod.is_virtual) {
- return OK; // Ignore
- }
-
- bool ret_void = p_imethod.return_type.cname == name_cache.type_void;
-
- const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
-
- String argc_str = itos(p_imethod.arguments.size());
-
- String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND;
- if (!p_imethod.is_static) {
- c_func_sig += ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE;
- }
- String c_in_statements;
- String c_args_var_content;
-
// Get arguments information
int i = 0;
- for (const ArgumentInterface &iarg : p_imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ for (const TypeReference &arg_type_ref : p_icall.argument_types) {
+ const TypeInterface *arg_type = _get_type_or_null(arg_type_ref);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Return type not found
String c_param_name = "arg" + itos(i + 1);
- if (p_imethod.is_vararg) {
- if (i < p_imethod.arguments.size() - 1) {
- c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
- c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(";
- c_in_statements += itos(i);
- c_in_statements += sformat(", &%s_in);\n", c_param_name);
+ if (p_icall.is_vararg) {
+ if (i < p_icall.get_arguments_count() - 1) {
+ String c_in_vararg = arg_type->c_in_vararg;
+
+ if (arg_type->is_object_type) {
+ c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObjectPtr(%1);\n";
+ }
+
+ ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG,
+ "VarArg support not implemented for parameter type: " + arg_type->name);
+
+ c_in_statements
+ << sformat(c_in_vararg, return_type->c_type, c_param_name,
+ String(), String(), String(), INDENT3)
+ << INDENT3 C_LOCAL_PTRCALL_ARGS "[" << itos(i)
+ << "] = new IntPtr(&" << c_param_name << "_in);\n";
}
} else {
if (i > 0) {
- c_args_var_content += ", ";
+ c_args_var_content << ", ";
}
if (arg_type->c_in.size()) {
- c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
+ c_in_statements << sformat(arg_type->c_in, arg_type->c_type, c_param_name,
+ String(), String(), String(), INDENT2);
}
- c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
+ c_args_var_content << sformat(arg_type->c_arg_in, c_param_name);
}
- c_func_sig += ", ";
- c_func_sig += arg_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += c_param_name;
+ c_func_sig << ", " << arg_type->c_type_in << " " << c_param_name;
i++;
}
- if (return_type->ret_as_byref_arg) {
- c_func_sig += ", ";
- c_func_sig += return_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += "arg_ret";
+ String icall_method = p_icall.name;
- i++;
- }
-
- HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
- ERR_FAIL_NULL_V(match, ERR_BUG);
+ // Generate icall function
- const InternalCall *im_icall = match->value;
- String icall_method = im_icall->name;
+ r_output << MEMBER_BEGIN "internal static unsafe " << (ret_void ? "void" : return_type->c_type_out) << " "
+ << icall_method << "(" << c_func_sig.as_string() << ") " OPEN_BLOCK;
- if (!generated_icall_funcs.find(im_icall)) {
- generated_icall_funcs.push_back(im_icall);
+ if (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) {
+ String ptrcall_return_type;
+ String initialization;
- if (im_icall->editor_only) {
- p_output.append("#ifdef TOOLS_ENABLED\n");
+ if (return_type->is_object_type) {
+ ptrcall_return_type = return_type->is_ref_counted ? "godot_ref" : return_type->c_type;
+ initialization = " = default";
+ } else {
+ ptrcall_return_type = return_type->c_type;
}
- // Generate icall function
+ r_output << INDENT2;
- p_output.append((ret_void || return_type->ret_as_byref_arg) ? "void " : return_type->c_type_out + " ");
- p_output.append(icall_method);
- p_output.append("(");
- p_output.append(c_func_sig);
- p_output.append(") " OPEN_BLOCK);
+ if (return_type->is_ref_counted || return_type->c_type_is_disposable_struct) {
+ r_output << "using ";
- if (!ret_void) {
- String ptrcall_return_type;
- String initialization;
-
- if (p_imethod.is_vararg && return_type->cname != name_cache.type_Variant) {
- // VarArg methods always return Variant, but there are some cases in which MethodInfo provides
- // a specific return type. We trust this information is valid. We need a temporary local to keep
- // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
- // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
- // Alternatively, we could just return Variant, but that would result in a worse API.
- p_output.append("\tVariant " C_LOCAL_VARARG_RET ";\n");
+ if (initialization.is_empty()) {
+ initialization = " = default";
}
+ } else if (return_type->c_ret_needs_default_initialization) {
+ initialization = " = default";
+ }
- if (return_type->is_object_type) {
- ptrcall_return_type = return_type->is_ref_counted ? "Ref<RefCounted>" : return_type->c_type;
- initialization = return_type->is_ref_counted ? "" : " = nullptr";
- } else {
- ptrcall_return_type = return_type->c_type;
- }
+ r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n";
+ }
- p_output.append("\t" + ptrcall_return_type);
- p_output.append(" " C_LOCAL_RET);
- p_output.append(initialization + ";\n");
+ if (!p_icall.is_static) {
+ r_output << INDENT2 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n"
+ << INDENT3 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n";
+ }
- String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "nullptr" : return_type->c_type_out + "()";
+ String argc_str = itos(p_icall.get_arguments_count());
- if (!p_imethod.is_static) {
- if (return_type->ret_as_byref_arg) {
- p_output.append("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = ");
- p_output.append(fail_ret);
- p_output.append("; ERR_FAIL_MSG(\"Parameter ' " CS_PARAM_INSTANCE " ' is null.\"); }\n");
- } else {
- p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", ");
- p_output.append(fail_ret);
- p_output.append(");\n");
- }
- }
- } else {
- if (!p_imethod.is_static) {
- p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
- }
- }
-
- if (p_imethod.arguments.size()) {
- if (p_imethod.is_vararg) {
- String vararg_arg = "arg" + argc_str;
- String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg
-
- p_output.append("\tint vararg_length = mono_array_length(");
- p_output.append(vararg_arg);
- p_output.append(");\n\tint total_length = ");
- p_output.append(real_argc_str);
- p_output.append(" + vararg_length;\n"
- "\tArgumentsVector<Variant> varargs(vararg_length);\n"
- "\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n");
- p_output.append(c_in_statements);
- p_output.append("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
- "\t\tMonoObject* elem = mono_array_get(");
- p_output.append(vararg_arg);
- p_output.append(", MonoObject*, i);\n"
- "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
- "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
- p_output.append(real_argc_str);
- p_output.append(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK);
- } else {
- p_output.append(c_in_statements);
- p_output.append("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
- p_output.append(argc_str + "] = { ");
- p_output.append(c_args_var_content + " };\n");
- }
- }
+ auto generate_call_and_return_stmts = [&](const char *base_indent) {
+ if (p_icall.is_vararg) {
+ // MethodBind Call
+ r_output << base_indent;
- if (p_imethod.is_vararg) {
- p_output.append("\tCallable::CallError vcall_error;\n\t");
+ // VarArg methods always return Variant, but there are some cases in which MethodInfo provides
+ // a specific return type. We trust this information is valid. We need a temporary local to keep
+ // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
+ // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
+ // Alternatively, we could just return Variant, but that would result in a worse API.
if (!ret_void) {
- // See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.append(C_LOCAL_VARARG_RET " = ");
+ r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = ";
} else {
- p_output.append(C_LOCAL_RET " = ");
+ r_output << "using godot_variant " << C_LOCAL_RET " = ";
}
}
- p_output.append(CS_PARAM_METHODBIND "->call(");
- p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE);
- p_output.append(", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "nullptr");
- p_output.append(", total_length, vcall_error);\n");
+ r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call("
+ << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
+ << ", total_length, out _);\n";
if (!ret_void) {
- // See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.append("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n");
+ if (return_type->cname == name_cache.enum_Error) {
+ r_output << base_indent << C_LOCAL_RET " = VariantUtils.ConvertToInt64(" C_LOCAL_VARARG_RET ");\n";
+ } else {
+ // TODO: Use something similar to c_in_vararg (see usage above, with error if not implemented)
+ CRASH_NOW_MSG("Custom VarArg return type not implemented: " + return_type->name);
+ r_output << base_indent << C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n";
+ }
}
}
} else {
- p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(");
- p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE);
- p_output.append(", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "nullptr, ");
- p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "nullptr);\n");
+ // MethodBind PtrCall
+ r_output << base_indent << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_ptrcall("
+ << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? C_LOCAL_PTRCALL_ARGS : "null")
+ << ", " << (!ret_void ? "&" C_LOCAL_RET ");\n" : "null);\n");
}
+ // Return statement
+
if (!ret_void) {
if (return_type->c_out.is_empty()) {
- p_output.append("\treturn " C_LOCAL_RET ";\n");
- } else if (return_type->ret_as_byref_arg) {
- p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret"));
+ r_output << base_indent << "return " C_LOCAL_RET ";\n";
} else {
- p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name));
+ r_output << sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET,
+ return_type->name, String(), String(), base_indent);
}
}
+ };
+
+ if (p_icall.get_arguments_count()) {
+ if (p_icall.is_vararg) {
+ String vararg_arg = "arg" + argc_str;
+ String real_argc_str = itos(p_icall.get_arguments_count() - 1); // Arguments count without vararg
+
+ p_icall.get_arguments_count();
+
+ r_output << INDENT2 "int vararg_length = " << vararg_arg << ".Length;\n"
+ << INDENT2 "int total_length = " << real_argc_str << " + vararg_length;\n";
+
+ r_output << INDENT2 "Span<godot_variant.movable> varargs_span = vararg_length <= VarArgsSpanThreshold ?\n"
+ << INDENT3 "stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() :\n"
+ << INDENT3 "new godot_variant.movable[vararg_length];\n";
+
+ r_output << INDENT2 "Span<IntPtr> " C_LOCAL_PTRCALL_ARGS "_span = total_length <= VarArgsSpanThreshold ?\n"
+ << INDENT3 "stackalloc IntPtr[VarArgsSpanThreshold] :\n"
+ << INDENT3 "new IntPtr[total_length];\n";
+
+ r_output << INDENT2 "using var variantSpanDisposer = new VariantSpanDisposer(varargs_span);\n";
- p_output.append(CLOSE_BLOCK "\n");
+ r_output << INDENT2 "fixed (godot_variant.movable* varargs = &MemoryMarshal.GetReference(varargs_span))\n"
+ << INDENT2 "fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = "
+ "&MemoryMarshal.GetReference(" C_LOCAL_PTRCALL_ARGS "_span))\n"
+ << OPEN_BLOCK_L2;
- if (im_icall->editor_only) {
- p_output.append("#endif // TOOLS_ENABLED\n");
+ r_output << c_in_statements.as_string();
+
+ r_output << INDENT3 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
+ << INDENT4 "varargs[i] = " << vararg_arg << "[i].NativeVar;\n"
+ << INDENT4 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n"
+ << CLOSE_BLOCK_L3;
+
+ generate_call_and_return_stmts(INDENT3);
+
+ r_output << CLOSE_BLOCK_L2;
+ } else {
+ r_output << c_in_statements.as_string();
+
+ r_output << INDENT2 "void** " C_LOCAL_PTRCALL_ARGS " = stackalloc void*["
+ << argc_str << "] { " << c_args_var_content.as_string() << " };\n";
+
+ generate_call_and_return_stmts(INDENT2);
}
+ } else {
+ generate_call_and_return_stmts(INDENT2);
}
+ r_output << CLOSE_BLOCK_L1;
+
+ return OK;
+}
+
+Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
+
+ file->store_string(p_content.as_string());
+
return OK;
}
@@ -2514,27 +2526,6 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
return nullptr;
}
-const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
- const TypeInterface *found = _get_type_or_null(p_typeref);
-
- if (found) {
- return found;
- }
-
- ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'.");
-
- HashMap<StringName, TypeInterface>::ConstIterator match = placeholder_types.find(p_typeref.cname);
-
- if (match) {
- return &match->value;
- }
-
- TypeInterface placeholder;
- TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
-
- return &placeholder_types.insert(placeholder.cname, placeholder)->value;
-}
-
const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) {
if (p_generic_type_parameters.is_empty()) {
return "";
@@ -2548,7 +2539,8 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface
int i = 0;
String params = "<";
for (const TypeReference &param_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 +2658,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 +2670,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 +2738,27 @@ 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.cs_variant_to_managed = "(%1)VariantUtils.ConvertToGodotObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromGodotObject(%0)";
+
+ itype.c_out = "%5return ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
- itype.c_out += itype.is_ref_counted ? "(%1.ptr());\n" : "(%1);\n";
+ itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n";
- itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)";
+ itype.cs_type = itype.proxy_name;
- itype.c_type = "Object*";
+ if (itype.is_singleton) {
+ itype.cs_in_expr = "Object." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")";
+ } else {
+ itype.cs_in_expr = "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 = "MonoObject*";
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = "IntPtr";
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_out = "Object";
// Populate properties
@@ -2846,6 +2849,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
if (method_info.flags & METHOD_FLAG_VIRTUAL) {
imethod.is_virtual = true;
+ itype.has_virtual_methods = true;
}
PropertyInfo return_info = method_info.return_val;
@@ -2887,7 +2891,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 +2923,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;
@@ -2985,7 +2989,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ERR_FAIL_COND_V_MSG(itype.find_property_by_name(imethod.cname), false,
"Method name conflicts with property: '" + itype.name + "." + imethod.name + "'.");
- // Classes starting with an underscore are ignored unless they're used as a property setter or getter
+ // Methods starting with an underscore are ignored unless they're used as a property setter or getter
if (!imethod.is_virtual && imethod.name[0] == '_') {
for (const PropertyInterface &iprop : itype.properties) {
if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
@@ -3027,7 +3031,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;
@@ -3132,6 +3136,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_itype.cname = StringName(enum_itype.name);
enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
TypeInterface::postsetup_enum_type(enum_itype);
+ enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
+ enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
}
@@ -3169,7 +3175,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
switch (p_val.get_type()) {
case Variant::NIL:
// Either Object type or Variant
- r_iarg.default_argument = "null";
+ r_iarg.default_argument = "default";
break;
// Atomic types
case Variant::BOOL:
@@ -3189,8 +3195,13 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
case Variant::STRING_NAME:
case Variant::NODE_PATH:
if (r_iarg.type.cname == name_cache.type_StringName || r_iarg.type.cname == name_cache.type_NodePath) {
- r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ if (r_iarg.default_argument.length() > 0) {
+ r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ } else {
+ // No need for a special `in` statement to change `null` to `""`. Marshaling takes care of this already.
+ r_iarg.default_argument = "null";
+ }
} else {
CRASH_COND(r_iarg.type.cname != name_cache.type_String);
r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
@@ -3236,8 +3247,11 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "null";
break;
case Variant::DICTIONARY:
- r_iarg.default_argument = "new %s()";
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ ERR_FAIL_COND_V_MSG(!p_val.operator Dictionary().is_empty(), false,
+ "Default value of type 'Dictionary' must be an empty dictionary.");
+ // The [cs_in] expression already interprets null values as empty dictionaries.
+ r_iarg.default_argument = "null";
+ r_iarg.def_param_mode = ArgumentInterface::CONSTANT;
break;
case Variant::RID:
ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_RID, false,
@@ -3246,11 +3260,14 @@ 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 { }";
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ ERR_FAIL_COND_V_MSG(!p_val.operator Array().is_empty(), false,
+ "Default value of type 'Array' must be an empty array.");
+ // The [cs_in] expression already interprets null values as empty arrays.
+ r_iarg.default_argument = "null";
+ r_iarg.def_param_mode = ArgumentInterface::CONSTANT;
break;
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
@@ -3325,12 +3342,12 @@ 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;
}
- if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") {
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "default") {
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
}
return true;
@@ -3344,16 +3361,12 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
#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; \
+ itype.c_type_in = #m_type "*"; \
+ itype.c_type_out = itype.cs_type; \
+ itype.cs_in_expr = "&%0"; \
+ itype.cs_in_expr_is_unsafe = true; \
+ itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)"; \
+ itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
builtin_types.insert(itype.cname, itype); \
}
@@ -3370,54 +3383,60 @@ 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.cs_in_expr = "%0.ToGodotBool()";
+ itype.cs_out = "%5return %0(%1).ToBool();";
+ 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";
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToBool(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromBool(%0)";
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, m_int_struct_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"; \
+ itype.cs_variant_to_managed = "VariantUtils.ConvertTo" m_int_struct_name "(%0)"; \
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromInt(%0)"; \
+ 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", "Int8");
+ INSERT_INT_TYPE("short", "Int16");
+ INSERT_INT_TYPE("int", "Int32");
+ INSERT_INT_TYPE("long", "Int64");
+ INSERT_INT_TYPE("byte", "UInt8");
+ INSERT_INT_TYPE("ushort", "UInt16");
+ INSERT_INT_TYPE("uint", "UInt32");
+ INSERT_INT_TYPE("ulong", "UInt64");
+
+#undef INSERT_INT_TYPE
}
// Floating point types
@@ -3427,18 +3446,19 @@ 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";
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat32(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
builtin_types.insert(itype.cname, itype);
// double
@@ -3446,15 +3466,14 @@ 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";
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat64(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)";
builtin_types.insert(itype.cname, itype);
}
@@ -3463,15 +3482,17 @@ 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.CreateFromString(%1);\n";
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToStringObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromString(%0)";
builtin_types.insert(itype.cname, itype);
// StringName
@@ -3479,17 +3500,19 @@ 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_expr = "(%1)(%0?.NativeValue ?? default)";
+ // Cannot pass null StringName to ptrcall
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_string_name";
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n";
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToStringNameObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromStringName(%0)";
builtin_types.insert(itype.cname, itype);
// NodePath
@@ -3497,15 +3520,18 @@ 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_expr = "(%1)(%0?.NativeValue ?? default)";
+ // Cannot pass null NodePath to ptrcall
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_node_path";
+ itype.c_type_in = 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;
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToNodePathObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromNodePath(%0)";
builtin_types.insert(itype.cname, itype);
// RID
@@ -3513,45 +3539,45 @@ 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;
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToRID(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromRID(%0)";
builtin_types.insert(itype.cname, itype);
// Variant
itype = TypeInterface();
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.proxy_name = "Variant";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "object";
- itype.im_type_out = itype.proxy_name;
+ itype.c_in = "%5%0 %1_in = (%0)%1.NativeVar;\n";
+ itype.c_out = "%5return Variant.CreateTakingOwnershipOfDisposableValue(%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 = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
+ itype.cs_variant_to_managed = "Variant.CreateCopyingBorrowed(%0)";
+ itype.cs_managed_to_variant = "%0.CopyNativeVariant()";
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.cs_in_expr = "%0";
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(in %1);\n";
+ itype.c_out = "%5return " C_METHOD_MANAGED_FROM_CALLABLE "(in %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_type = "godot_callable";
+ itype.c_type_in = "in " + itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToCallableManaged(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromCallable(%0)";
builtin_types.insert(itype.cname, itype);
// Signal
@@ -3559,66 +3585,65 @@ 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_expr = "%0";
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(in %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 = "in " + itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToSignalInfo(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromSignalInfo(%0)";
builtin_types.insert(itype.cname, itype);
// VarArg (fictitious type to represent variable arguments)
itype = TypeInterface();
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.proxy_name = "Variant[]";
+ itype.cs_type = "params Variant[]";
+ itype.cs_in_expr = "%0 ?? Array.Empty<Variant>()";
+ // 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 = "Array";
- itype.c_type_in = "MonoArray*";
- itype.cs_type = "params object[]";
- itype.im_type_in = "object[]";
+ itype.c_type_in = "Variant[]";
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; \
+ itype.cs_variant_to_managed = "VariantUtils.ConvertAs%2ToSystemArray(%0)"; \
+ itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
+ 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 +3653,24 @@ 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_expr = "(%1)(%0 ?? new()).NativeValue";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_array";
+ itype.c_type_in = 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;
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
+ 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 +3679,24 @@ 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_expr = "(%1)(%0 ?? new()).NativeValue";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_dictionary";
+ itype.c_type_in = 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;
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
+ 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 +3704,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);
}
@@ -3721,6 +3762,8 @@ void BindingsGenerator::_populate_global_constants() {
enum_itype.cname = ienum.cname;
enum_itype.proxy_name = enum_itype.name;
TypeInterface::postsetup_enum_type(enum_itype);
+ enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
+ enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
int prefix_length = _determine_enum_prefix(ienum);
@@ -3753,6 +3796,8 @@ void BindingsGenerator::_populate_global_constants() {
enum_itype.cname = enum_cname;
enum_itype.proxy_name = enum_itype.name;
TypeInterface::postsetup_enum_type(enum_itype);
+ enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)";
+ enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)";
enum_types.insert(enum_itype.cname, enum_itype);
}
}
@@ -3791,21 +3836,18 @@ void BindingsGenerator::_initialize() {
// Generate internal calls (after populating type interfaces and global constants)
- core_custom_icalls.clear();
- editor_custom_icalls.clear();
-
for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
- _generate_method_icalls(E.value);
+ const TypeInterface &itype = E.value;
+ Error err = _populate_method_icalls_table(itype);
+ ERR_FAIL_COND_MSG(err != OK, "Failed to generate icalls table for type: " + itype.name);
}
initialized = true;
}
static String generate_all_glue_option = "--generate-mono-glue";
-static String generate_cs_glue_option = "--generate-mono-cs-glue";
-static String generate_cpp_glue_option = "--generate-mono-cpp-glue";
-static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, String cpp_dir_path) {
+static void handle_cmdline_options(String glue_dir_path) {
BindingsGenerator bindings_generator;
bindings_generator.set_log_print_enabled(true);
@@ -3814,43 +3856,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 (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) {
+ ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
}
+}
- if (cpp_dir_path.length()) {
- if (bindings_generator.generate_glue(cpp_dir_path) != OK) {
- ERR_PRINT(generate_cpp_glue_option + ": Failed to generate the C++ glue.");
- }
- }
+static void cleanup_and_exit_godot() {
+ // Exit once done
+ Main::cleanup(true);
+ ::exit(0);
}
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
- const int NUM_OPTIONS = 2;
-
String glue_dir_path;
- String cs_dir_path;
- String cpp_dir_path;
-
- int options_left = NUM_OPTIONS;
-
- bool exit_godot = false;
const List<String>::Element *elem = p_cmdline_args.front();
- while (elem && options_left) {
+ while (elem) {
if (elem->get() == generate_all_glue_option) {
const List<String>::Element *path_elem = elem->next();
@@ -3859,48 +3883,20 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
} else {
ERR_PRINT(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue').");
- exit_godot = true;
- }
-
- --options_left;
- } else if (elem->get() == generate_cs_glue_option) {
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- cs_dir_path = path_elem->get();
- elem = elem->next();
- } else {
- ERR_PRINT(generate_cs_glue_option + ": No output directory specified.");
- exit_godot = true;
+ // Exit once done with invalid command line arguments
+ cleanup_and_exit_godot();
}
- --options_left;
- } else if (elem->get() == generate_cpp_glue_option) {
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- cpp_dir_path = path_elem->get();
- elem = elem->next();
- } else {
- ERR_PRINT(generate_cpp_glue_option + ": No output directory specified.");
- exit_godot = true;
- }
-
- --options_left;
+ break;
}
elem = elem->next();
}
- if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) {
- handle_cmdline_options(glue_dir_path, cs_dir_path, cpp_dir_path);
- exit_godot = true;
- }
-
- if (exit_godot) {
+ if (glue_dir_path.length()) {
+ handle_cmdline_options(glue_dir_path);
// Exit once done
- Main::cleanup(true);
- ::exit(0);
+ cleanup_and_exit_godot();
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index ee170e4558..c1295385dc 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -229,6 +229,23 @@ class BindingsGenerator {
bool is_ref_counted = false;
/**
+ * Determines whether the native return value of this type must be disposed
+ * by the generated internal call (think of `godot_string`, whose destructor
+ * must be called). Some structs that are disposable may still disable this
+ * flag if the ownership is transferred.
+ */
+ bool c_type_is_disposable_struct = false;
+
+ /**
+ * Determines whether the native return value of this type must be zero initialized
+ * before its address is passed to ptrcall. This is required for types whose destructor
+ * is called before being assigned the return value by `PtrToArg::encode`, e.g.:
+ * Array, Dictionary, String, StringName, Variant.
+ * It's not necessary to set this to `true` if [c_type_is_disposable_struct] is already `true`.
+ */
+ bool c_ret_needs_default_initialization = false;
+
+ /**
* Used only by Object-derived types.
* Determines if this type is not abstract (incomplete).
* e.g.: CanvasItem cannot be instantiated.
@@ -242,31 +259,34 @@ 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 transform 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 expression with 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;
/**
- * One or more statements that manipulate the parameter before being passed as argument of a ptrcall.
+ * One or more statements that transform 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].
- * 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;
+ String c_in_vararg;
/**
* Determines the expression that will be passed as argument to ptrcall.
@@ -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;
@@ -327,7 +348,21 @@ class BindingsGenerator {
* An expression that overrides the way the parameter is passed to the internal call.
* If empty, the parameter is passed as is.
* Formatting elements:
- * %0 or %s: name of the parameter
+ * %0: name of the parameter
+ * %1: [c_type] of the parameter
+ */
+ String cs_in_expr;
+ bool cs_in_expr_is_unsafe = false;
+
+ /**
+ * One or more statements that transform the parameter before being passed to the internal call.
+ * If the statement adds a local that must be passed as the argument instead of the parameter,
+ * the expression with the name of that local must be specified with [cs_in_expr].
+ * Formatting elements:
+ * %0: [c_type] of the parameter
+ * %1: name of the parameter
+ * %2-4: reserved
+ * %5: indentation text
*/
String cs_in;
@@ -338,7 +373,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;
@@ -349,14 +386,20 @@ class BindingsGenerator {
String cs_type;
/**
- * Type used for parameters of internal call methods.
+ * Formatting elements:
+ * %0: input expression of type `in godot_variant`
+ * %1: [cs_type] of this type
+ * %2: [name] of this type
*/
- String im_type_in;
+ String cs_variant_to_managed;
/**
- * Type used for the return type of internal call methods.
+ * Formatting elements:
+ * %0: input expression
+ * %1: [cs_type] of this type
+ * %2: [name] of this type
*/
- String im_type_out;
+ String cs_managed_to_variant;
const DocData::ClassDoc *class_doc = nullptr;
@@ -366,6 +409,8 @@ class BindingsGenerator {
List<MethodInterface> methods;
List<SignalInterface> signals_;
+ bool has_virtual_methods = false;
+
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
for (const MethodInterface &E : methods) {
if (E.cname == p_cname) {
@@ -432,8 +477,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 +512,27 @@ class BindingsGenerator {
return itype;
}
- static void create_placeholder_type(TypeInterface &r_itype, const StringName &p_cname) {
- r_itype.name = p_cname;
- r_itype.cname = p_cname;
- r_itype.proxy_name = r_itype.name;
-
- r_itype.c_type = r_itype.name;
- r_itype.c_type_in = "MonoObject*";
- r_itype.c_type_out = "MonoObject*";
- r_itype.cs_type = r_itype.proxy_name;
- r_itype.im_type_in = r_itype.proxy_name;
- r_itype.im_type_out = r_itype.proxy_name;
- }
-
- static void postsetup_enum_type(TypeInterface &r_enum_itype) {
- // C interface for enums is the same as that of 'uint32_t'. Remember to apply
- // any of the changes done here to the 'uint32_t' type interface as well.
-
- r_enum_itype.c_arg_in = "&%s_in";
- {
- // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
- r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- r_enum_itype.c_out = "\treturn (%0)%1;\n";
- r_enum_itype.c_type = "int64_t";
- }
- r_enum_itype.c_type_in = "int32_t";
- r_enum_itype.c_type_out = r_enum_itype.c_type_in;
-
- r_enum_itype.cs_type = r_enum_itype.proxy_name;
- r_enum_itype.cs_in = "(int)%s";
- r_enum_itype.cs_out = "return (%2)%0(%1);";
- r_enum_itype.im_type_in = "int";
- r_enum_itype.im_type_out = "int";
- r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
- }
+ static void postsetup_enum_type(TypeInterface &r_enum_itype);
TypeInterface() {}
};
struct InternalCall {
String name;
- String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq]
- String im_sig; // Signature for the C# method declaration
String unique_sig; // Unique signature to avoid duplicates in containers
bool editor_only = false;
- InternalCall() {}
+ bool is_vararg = false;
+ bool is_static = false;
+ TypeReference return_type;
+ List<TypeReference> argument_types;
- InternalCall(const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
- name = p_name;
- im_type_out = p_im_type_out;
- im_sig = p_im_sig;
- unique_sig = p_unique_sig;
- editor_only = false;
- }
+ _FORCE_INLINE_ int get_arguments_count() const { return argument_types.size(); }
+
+ InternalCall() {}
- InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
+ InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_unique_sig = String()) {
name = p_name;
- im_type_out = p_im_type_out;
- im_sig = p_im_sig;
unique_sig = p_unique_sig;
editor_only = api_type == ClassDB::API_EDITOR;
}
@@ -540,7 +547,6 @@ class BindingsGenerator {
HashMap<StringName, TypeInterface> obj_types;
- HashMap<StringName, TypeInterface> placeholder_types;
HashMap<StringName, TypeInterface> builtin_types;
HashMap<StringName, TypeInterface> enum_types;
@@ -548,13 +554,9 @@ class BindingsGenerator {
List<ConstantInterface> global_constants;
List<InternalCall> method_icalls;
+ /// Stores the unique internal calls from [method_icalls] that are assigned to each method.
HashMap<const MethodInterface *, const InternalCall *> method_icalls_map;
- List<const InternalCall *> generated_icall_funcs;
-
- List<InternalCall> core_custom_icalls;
- List<InternalCall> editor_custom_icalls;
-
HashMap<StringName, List<StringName>> blacklisted_methods;
void _initialize_blacklisted_methods();
@@ -571,6 +573,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 +599,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 +642,6 @@ class BindingsGenerator {
NameCache name_cache;
- const List<InternalCall>::Element *find_icall_by_name(const String &p_name, const List<InternalCall> &p_list) {
- const List<InternalCall>::Element *it = p_list.front();
- while (it) {
- if (it->get().name == p_name) {
- return it;
- }
- it = it->next();
- }
- return nullptr;
- }
-
const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
for (const ConstantInterface &E : p_constants) {
if (E.name == p_name) {
@@ -657,18 +652,38 @@ class BindingsGenerator {
return nullptr;
}
- inline String get_unique_sig(const TypeInterface &p_type) {
- if (p_type.is_ref_counted) {
- return "Ref";
- } else if (p_type.is_object_type) {
+ inline String get_arg_unique_sig(const TypeInterface &p_type) {
+ // For parameters, we treat reference and non-reference derived types the same.
+ if (p_type.is_object_type) {
return "Obj";
} else if (p_type.is_enum) {
return "int";
+ } else if (p_type.cname == name_cache.type_Array_generic) {
+ return "Array";
+ } else if (p_type.cname == name_cache.type_Dictionary_generic) {
+ return "Dictionary";
}
return p_type.name;
}
+ inline String get_ret_unique_sig(const TypeInterface *p_type) {
+ // Reference derived return types are treated differently.
+ if (p_type->is_ref_counted) {
+ return "Ref";
+ } else if (p_type->is_object_type) {
+ return "Obj";
+ } else if (p_type->is_enum) {
+ return "int";
+ } else if (p_type->cname == name_cache.type_Array_generic) {
+ return "Array";
+ } else if (p_type->cname == name_cache.type_Dictionary_generic) {
+ return "Dictionary";
+ }
+
+ return p_type->name;
+ }
+
String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype);
void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts);
@@ -682,10 +697,9 @@ class BindingsGenerator {
int _determine_enum_prefix(const EnumInterface &p_ienum);
void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);
- void _generate_method_icalls(const TypeInterface &p_itype);
+ Error _populate_method_icalls_table(const TypeInterface &p_itype);
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
- const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters);
@@ -706,11 +720,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 +735,12 @@ public:
Error generate_cs_core_project(const String &p_proj_dir);
Error generate_cs_editor_project(const String &p_proj_dir);
Error generate_cs_api(const String &p_output_dir);
- Error generate_glue(const String &p_output_dir);
_FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; }
_FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
_FORCE_INLINE_ bool is_initialized() { return initialized; }
- static uint32_t get_version();
-
static void handle_cmdline_args(const List<String> &p_cmdline_args);
BindingsGenerator() {
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index f830c7ffe1..1ef78c3ac2 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -46,179 +46,81 @@
#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"
#include "code_completion.h"
-#include "godotsharp_export.h"
-MonoString *godot_icall_GodotSharpDirs_ResDataDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResConfigDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResTempDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_MonoUserDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir());
-}
+#include "../interop_types.h"
-MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir());
-#else
- return nullptr;
+#ifdef __cplusplus
+extern "C" {
#endif
-}
-MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir());
-#else
- return nullptr;
-#endif
+void godot_icall_GodotSharpDirs_ResMetadataDir(godot_string *r_dest) {
+ memnew_placement(r_dest, String(GodotSharpDirs::get_res_metadata_dir()));
}
-MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
-#else
- return nullptr;
-#endif
+void godot_icall_GodotSharpDirs_MonoUserDir(godot_string *r_dest) {
+ memnew_placement(r_dest, String(GodotSharpDirs::get_mono_user_dir()));
}
-MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() {
+void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path());
+ memnew_placement(r_dest, String(GodotSharpDirs::get_build_logs_dir()));
#else
return nullptr;
#endif
}
-MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() {
+void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir());
+ memnew_placement(r_dest, String(GodotSharpDirs::get_data_editor_tools_dir()));
#else
return nullptr;
#endif
}
-MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir());
-#else
- return nullptr;
-#endif
-}
-
-MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() {
-#ifdef WINDOWS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir());
-#else
- return nullptr;
-#endif
-}
-
-void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) {
- String task = GDMonoMarshal::mono_string_to_godot(p_task);
- String label = GDMonoMarshal::mono_string_to_godot(p_label);
+void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) {
+ String task = *reinterpret_cast<const String *>(p_task);
+ String label = *reinterpret_cast<const String *>(p_label);
EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel);
}
-void godot_icall_EditorProgress_Dispose(MonoString *p_task) {
- String task = GDMonoMarshal::mono_string_to_godot(p_task);
+void godot_icall_EditorProgress_Dispose(const godot_string *p_task) {
+ String task = *reinterpret_cast<const String *>(p_task);
EditorNode::progress_end_task(task);
}
-MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) {
- String task = GDMonoMarshal::mono_string_to_godot(p_task);
- String state = GDMonoMarshal::mono_string_to_godot(p_state);
+bool godot_icall_EditorProgress_Step(const godot_string *p_task, const godot_string *p_state, int32_t p_step, bool p_force_refresh) {
+ String task = *reinterpret_cast<const String *>(p_task);
+ String state = *reinterpret_cast<const String *>(p_state);
return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
}
-uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies,
- MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) {
- Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies);
- String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
- String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
- Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies);
-
- return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies);
-}
-
-MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
- String config = GDMonoMarshal::mono_string_to_godot(p_config);
- String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config);
- return GDMonoMarshal::mono_string_from_godot(error_str);
-}
-
-MonoString *godot_icall_Internal_FullExportTemplatesDir() {
+void godot_icall_Internal_FullExportTemplatesDir(godot_string *r_dest) {
String full_templates_dir = EditorPaths::get_singleton()->get_export_templates_dir().plus_file(VERSION_FULL_CONFIG);
- return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
-}
-
-MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) {
- String path = GDMonoMarshal::mono_string_to_godot(p_path);
- return GDMonoMarshal::mono_string_from_godot(path.simplify_path());
+ memnew_placement(r_dest, String(full_templates_dir));
}
-MonoBoolean godot_icall_Internal_IsMacOSAppBundleInstalled(MonoString *p_bundle_id) {
+bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle_id) {
#ifdef MACOS_ENABLED
- String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id);
- return (MonoBoolean)macos_is_app_bundle_installed(bundle_id);
+ String bundle_id = *reinterpret_cast<const String *>(p_bundle_id);
+ return (bool)macos_is_app_bundle_installed(bundle_id);
#else
(void)p_bundle_id; // UNUSED
- return (MonoBoolean) false;
+ return (bool)false;
#endif
}
-MonoBoolean godot_icall_Internal_GodotIs32Bits() {
+bool godot_icall_Internal_GodotIs32Bits() {
return sizeof(void *) == 4;
}
-MonoBoolean godot_icall_Internal_GodotIsRealTDouble() {
+bool godot_icall_Internal_GodotIsRealTDouble() {
#ifdef REAL_T_IS_DOUBLE
- return (MonoBoolean) true;
+ return (bool)true;
#else
- return (MonoBoolean) false;
+ return (bool)false;
#endif
}
@@ -226,23 +128,15 @@ void godot_icall_Internal_GodotMainIteration() {
Main::iteration();
}
-uint64_t godot_icall_Internal_GetCoreApiHash() {
- return ClassDB::get_api_hash(ClassDB::API_CORE);
-}
-
-uint64_t godot_icall_Internal_GetEditorApiHash() {
- return ClassDB::get_api_hash(ClassDB::API_EDITOR);
-}
-
-MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() {
+bool godot_icall_Internal_IsAssembliesReloadingNeeded() {
#ifdef GD_MONO_HOT_RELOAD
- return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
+ return (bool)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
#else
- return (MonoBoolean) false;
+ return (bool)false;
#endif
}
-void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
+void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload);
#endif
@@ -252,24 +146,15 @@ void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
EditorDebuggerNode::get_singleton()->reload_scripts();
}
-MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
- Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource);
- return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
+bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) {
+ Ref<Resource> resource = p_resource;
+ return (bool)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
}
void godot_icall_Internal_EditorNodeShowScriptScreen() {
EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
}
-MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
-#ifdef WINDOWS_ENABLED
- String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
- return GDMonoMarshal::mono_string_from_godot(install_root_dir);
-#else
- return nullptr;
-#endif
-}
-
void godot_icall_Internal_EditorRunPlay() {
EditorNode::get_singleton()->run_play();
}
@@ -285,114 +170,93 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
}
}
-MonoArray *godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, MonoString *p_script_file) {
- String script_file = GDMonoMarshal::mono_string_to_godot(p_script_file);
+void godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, const godot_string *p_script_file, godot_packed_array *r_ret) {
+ String script_file = *reinterpret_cast<const String *>(p_script_file);
PackedStringArray suggestions = gdmono::get_code_completion((gdmono::CompletionKind)p_kind, script_file);
- return GDMonoMarshal::PackedStringArray_to_mono_array(suggestions);
+ memnew_placement(r_ret, PackedStringArray(suggestions));
}
float godot_icall_Globals_EditorScale() {
return EDSCALE;
}
-MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
- Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
+void godot_icall_Globals_GlobalDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
+ Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
- return GDMonoMarshal::variant_to_mono_object(result);
+ memnew_placement(r_result, Variant(result));
}
-MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
- Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
+void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
+ Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
- return GDMonoMarshal::variant_to_mono_object(result);
+ memnew_placement(r_result, Variant(result));
}
-MonoObject *godot_icall_Globals_EditorShortcut(MonoString *p_setting) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
+void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
Ref<Shortcut> result = ED_GET_SHORTCUT(setting);
- return GDMonoMarshal::variant_to_mono_object(result);
+ memnew_placement(r_result, Variant(result));
}
-MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
- String text = GDMonoMarshal::mono_string_to_godot(p_text);
- return GDMonoMarshal::mono_string_from_godot(TTR(text));
+void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) {
+ String text = *reinterpret_cast<const String *>(p_text);
+ memnew_placement(r_dest, String(TTR(text)));
}
-MonoString *godot_icall_Utils_OS_GetPlatformName() {
+void godot_icall_Utils_OS_GetPlatformName(godot_string *r_dest) {
String os_name = OS::get_singleton()->get_name();
- return GDMonoMarshal::mono_string_from_godot(os_name);
+ memnew_placement(r_dest, String(os_name));
}
-MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_path) {
+bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file_path) {
#ifdef UNIX_ENABLED
- String file_path = GDMonoMarshal::mono_string_to_godot(p_file_path);
+ String file_path = *reinterpret_cast<const String *>(p_file_path);
return access(file_path.utf8().get_data(), X_OK) == 0;
#else
ERR_FAIL_V(false);
#endif
}
-void register_editor_internal_calls() {
- // GodotSharpDirs
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir);
-
- // EditorProgress
- GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create);
- GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", godot_icall_EditorProgress_Dispose);
- GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", godot_icall_EditorProgress_Step);
-
- // ExportPlugin
- GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
-
- // Internals
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullExportTemplatesDir", godot_icall_Internal_FullExportTemplatesDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsMacOSAppBundleInstalled", godot_icall_Internal_IsMacOSAppBundleInstalled);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", godot_icall_Internal_ScriptEditorEdit);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", godot_icall_Internal_EditorNodeShowScriptScreen);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", godot_icall_Internal_MonoWindowsInstallRoot);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", godot_icall_Internal_EditorRunPlay);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", godot_icall_Internal_EditorRunStop);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", godot_icall_Internal_ScriptEditorDebugger_ReloadScripts);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", godot_icall_Internal_CodeCompletionRequest);
-
- // Globals
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorShortcut", godot_icall_Globals_EditorShortcut);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR);
-
- // Utils.OS
- GDMonoUtils::add_internal_call("GodotTools.Utils.OS::GetPlatformName", godot_icall_Utils_OS_GetPlatformName);
- GDMonoUtils::add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", godot_icall_Utils_OS_UnixFileHasExecutableAccess);
+#ifdef __cplusplus
+}
+#endif
+
+// The order in this array must match the declaration order of
+// the methods in 'GodotTools/Internals/Internal.cs'.
+static const void *unmanaged_callbacks[]{
+ (void *)godot_icall_GodotSharpDirs_ResMetadataDir,
+ (void *)godot_icall_GodotSharpDirs_MonoUserDir,
+ (void *)godot_icall_GodotSharpDirs_BuildLogsDirs,
+ (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir,
+ (void *)godot_icall_EditorProgress_Create,
+ (void *)godot_icall_EditorProgress_Dispose,
+ (void *)godot_icall_EditorProgress_Step,
+ (void *)godot_icall_Internal_FullExportTemplatesDir,
+ (void *)godot_icall_Internal_IsMacOSAppBundleInstalled,
+ (void *)godot_icall_Internal_GodotIs32Bits,
+ (void *)godot_icall_Internal_GodotIsRealTDouble,
+ (void *)godot_icall_Internal_GodotMainIteration,
+ (void *)godot_icall_Internal_IsAssembliesReloadingNeeded,
+ (void *)godot_icall_Internal_ReloadAssemblies,
+ (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts,
+ (void *)godot_icall_Internal_ScriptEditorEdit,
+ (void *)godot_icall_Internal_EditorNodeShowScriptScreen,
+ (void *)godot_icall_Internal_EditorRunPlay,
+ (void *)godot_icall_Internal_EditorRunStop,
+ (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts,
+ (void *)godot_icall_Internal_CodeCompletionRequest,
+ (void *)godot_icall_Globals_EditorScale,
+ (void *)godot_icall_Globals_GlobalDef,
+ (void *)godot_icall_Globals_EditorDef,
+ (void *)godot_icall_Globals_EditorShortcut,
+ (void *)godot_icall_Globals_TTR,
+ (void *)godot_icall_Utils_OS_GetPlatformName,
+ (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess,
+};
+
+const void **godotsharp::get_editor_interop_funcs(int32_t &r_size) {
+ r_size = sizeof(unmanaged_callbacks);
+ return unmanaged_callbacks;
}
diff --git a/modules/mono/editor/editor_internal_calls.h b/modules/mono/editor/editor_internal_calls.h
index 8262ac211a..35391f1f04 100644
--- a/modules/mono/editor/editor_internal_calls.h
+++ b/modules/mono/editor/editor_internal_calls.h
@@ -31,6 +31,10 @@
#ifndef EDITOR_INTERNAL_CALLS_H
#define EDITOR_INTERNAL_CALLS_H
-void register_editor_internal_calls();
+#include "core/typedefs.h"
+
+namespace godotsharp {
+const void **get_editor_interop_funcs(int32_t &r_size);
+}
#endif // EDITOR_INTERNAL_CALLS_H
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
deleted file mode 100644
index f9ea403334..0000000000
--- a/modules/mono/editor/godotsharp_export.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*************************************************************************/
-/* godotsharp_export.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 "godotsharp_export.h"
-
-#include <mono/metadata/image.h>
-
-#include "core/config/project_settings.h"
-#include "core/io/file_access_pack.h"
-#include "core/os/os.h"
-
-#include "../mono_gd/gd_mono.h"
-#include "../mono_gd/gd_mono_assembly.h"
-#include "../mono_gd/gd_mono_cache.h"
-#include "../utils/macros.h"
-
-namespace GodotSharpExport {
-
-MonoAssemblyName *new_mono_assembly_name() {
- // Mono has no public API to create an empty MonoAssemblyName and the struct is private.
- // As such the only way to create it is with a stub name and then clear it.
-
- MonoAssemblyName *aname = mono_assembly_name_new("stub");
- CRASH_COND(aname == nullptr);
- mono_assembly_name_free(aname); // Frees the string fields, not the struct
- return aname;
-}
-
-struct AssemblyRefInfo {
- String name;
- uint16_t major = 0;
- uint16_t minor = 0;
- uint16_t build = 0;
- uint16_t revision = 0;
-};
-
-AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
- const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
-
- uint32_t cols[MONO_ASSEMBLYREF_SIZE];
-
- mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
-
- return {
- String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])),
- (uint16_t)cols[MONO_ASSEMBLYREF_MAJOR_VERSION],
- (uint16_t)cols[MONO_ASSEMBLYREF_MINOR_VERSION],
- (uint16_t)cols[MONO_ASSEMBLYREF_BUILD_NUMBER],
- (uint16_t)cols[MONO_ASSEMBLYREF_REV_NUMBER]
- };
-}
-
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *reusable_aname, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
- MonoImage *image = p_assembly->get_image();
-
- for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
- AssemblyRefInfo ref_info = get_assemblyref_name(image, i);
-
- const String &ref_name = ref_info.name;
-
- if (r_assembly_dependencies.has(ref_name)) {
- continue;
- }
-
- mono_assembly_get_assemblyref(image, i, reusable_aname);
-
- GDMonoAssembly *ref_assembly = nullptr;
- if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
- ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
- }
-
- r_assembly_dependencies[ref_name] = ref_assembly->get_path();
-
- Error err = get_assembly_dependencies(ref_assembly, reusable_aname, p_search_dirs, r_assembly_dependencies);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
- }
-
- return OK;
-}
-
-Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
- const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_assembly_dependencies) {
- MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
- ERR_FAIL_NULL_V(export_domain, FAILED);
- _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
-
- _GDMONO_SCOPE_DOMAIN_(export_domain);
-
- Vector<String> search_dirs;
- GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
-
- if (p_custom_bcl_dir.length()) {
- // Only one mscorlib can be loaded. We need this workaround to make sure we get it from the right BCL directory.
- r_assembly_dependencies["mscorlib"] = p_custom_bcl_dir.plus_file("mscorlib.dll").simplify_path();
- }
-
- for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
- String assembly_name = *key;
- String assembly_path = p_initial_assemblies[*key];
-
- GDMonoAssembly *assembly = nullptr;
- bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
-
- ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
-
- MonoAssemblyName *reusable_aname = new_mono_assembly_name();
- SCOPE_EXIT { mono_free(reusable_aname); };
-
- Error err = get_assembly_dependencies(assembly, reusable_aname, search_dirs, r_assembly_dependencies);
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-} // namespace GodotSharpExport
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
deleted file mode 100644
index 60620b5f4d..0000000000
--- a/modules/mono/editor/godotsharp_export.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*************************************************************************/
-/* godotsharp_export.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 GODOTSHARP_EXPORT_H
-#define GODOTSHARP_EXPORT_H
-
-#include "core/error/error_list.h"
-#include "core/string/ustring.h"
-#include "core/variant/dictionary.h"
-
-#include "../mono_gd/gd_mono_header.h"
-
-namespace GodotSharpExport {
-
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
-
-Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
- const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies);
-} // namespace GodotSharpExport
-
-#endif // GODOTSHARP_EXPORT_H
diff --git a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs
index 2ca81ab7cd..c34f1a17f3 100644
--- a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs
+++ b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs
@@ -9,7 +9,7 @@ public partial class _CLASS_ : _BASE_
public const float JumpVelocity = -400.0f;
// Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
- public float gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
+ public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
public override void _PhysicsProcess(float delta)
{
diff --git a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs
index a6935fe497..069908c426 100644
--- a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs
+++ b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs
@@ -9,7 +9,7 @@ public partial class _CLASS_ : _BASE_
public const float JumpVelocity = 4.5f;
// Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
- public float gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
+ public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle();
public override void _PhysicsProcess(float delta)
{
@@ -26,7 +26,7 @@ public partial class _CLASS_ : _BASE_
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
- Vector3 direction = Transform.basis.Xform(new Vector3(inputDir.x, 0, inputDir.y)).Normalized();
+ Vector3 direction = (Transform.basis * new Vector3(inputDir.x, 0, inputDir.y)).Normalized();
if (direction != Vector3.Zero)
{
velocity.x = direction.x * Speed;
diff --git a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
index a1b93e7daa..bb482e0d6a 100644
--- a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
+++ b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
@@ -55,7 +55,7 @@ public partial class VisualShaderNode_CLASS_ : _BASE_
return 0;
}
- public override string _GetCode(Godot.Collections.Array inputVars, Godot.Collections.Array outputVars, Shader.Mode mode, VisualShader.Type type)
+ public override string _GetCode(Godot.Collections.Array<string> inputVars, Godot.Collections.Array<string> outputVars, Shader.Mode mode, VisualShader.Type type)
{
return "";
}
diff --git a/modules/mono/glue/GodotSharp/.editorconfig b/modules/mono/glue/GodotSharp/.editorconfig
new file mode 100644
index 0000000000..d4e71b1bd9
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/.editorconfig
@@ -0,0 +1,8 @@
+[**/Generated/**.cs]
+# Validate parameter is non-null before using it
+# Useful for generated code, as it disables nullable
+dotnet_diagnostic.CA1062.severity = error
+# CA1069: Enums should not have duplicate values
+dotnet_diagnostic.CA1069.severity = none
+# CA1708: Identifiers should differ by more than case
+dotnet_diagnostic.CA1708.severity = none
diff --git a/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml b/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml
new file mode 100644
index 0000000000..2dc350d4f2
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml
@@ -0,0 +1,5 @@
+<assembly name="System.Runtime.InteropServices">
+ <member name="T:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute">
+ <attribute ctor="M:JetBrains.Annotations.MeansImplicitUseAttribute.#ctor" />
+ </member>
+</assembly>
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs
new file mode 100644
index 0000000000..686023a077
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs
@@ -0,0 +1,24 @@
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal struct CallbacksData
+{
+ public CallbacksData(INamedTypeSymbol nativeTypeSymbol, INamedTypeSymbol funcStructSymbol)
+ {
+ NativeTypeSymbol = nativeTypeSymbol;
+ FuncStructSymbol = funcStructSymbol;
+ Methods = NativeTypeSymbol.GetMembers()
+ .Where(symbol => symbol is IMethodSymbol { IsPartialDefinition: true })
+ .Cast<IMethodSymbol>()
+ .ToImmutableArray();
+ }
+
+ public INamedTypeSymbol NativeTypeSymbol { get; }
+
+ public INamedTypeSymbol FuncStructSymbol { get; }
+
+ public ImmutableArray<IMethodSymbol> Methods { get; }
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs
new file mode 100644
index 0000000000..16e96c725a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs
@@ -0,0 +1,65 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal static class Common
+{
+ public static void ReportNonPartialUnmanagedCallbacksClass(
+ GeneratorExecutionContext context,
+ ClassDeclarationSyntax cds, INamedTypeSymbol symbol
+ )
+ {
+ string message =
+ "Missing partial modifier on declaration of type '" +
+ $"{symbol.FullQualifiedName()}' which has attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
+
+ string description = $"{message}. Classes with attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' " +
+ "must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0001",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ cds.GetLocation(),
+ cds.SyntaxTree.FilePath));
+ }
+
+ public static void ReportNonPartialUnmanagedCallbacksOuterClass(
+ GeneratorExecutionContext context,
+ TypeDeclarationSyntax outerTypeDeclSyntax
+ )
+ {
+ var outerSymbol = context.Compilation
+ .GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
+ .GetDeclaredSymbol(outerTypeDeclSyntax);
+
+ string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
+ namedTypeSymbol.FullQualifiedName() :
+ "type not found";
+
+ string message =
+ $"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
+ $"which contains one or more subclasses with attribute " +
+ $"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
+
+ string description = $"{message}. Classes with attribute " +
+ $"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' and their " +
+ "containing types must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0002",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ outerTypeDeclSyntax.GetLocation(),
+ outerTypeDeclSyntax.SyntaxTree.FilePath));
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
new file mode 100644
index 0000000000..fac362479a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
@@ -0,0 +1,119 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal static class ExtensionMethods
+{
+ public static AttributeData? GetGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
+ => symbol.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false);
+
+ private static bool HasGenerateUnmanagedCallbacksAttribute(
+ this ClassDeclarationSyntax cds, Compilation compilation,
+ out INamedTypeSymbol? symbol
+ )
+ {
+ var sm = compilation.GetSemanticModel(cds.SyntaxTree);
+
+ var classTypeSymbol = sm.GetDeclaredSymbol(cds);
+ if (classTypeSymbol == null)
+ {
+ symbol = null;
+ return false;
+ }
+
+ if (!classTypeSymbol.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false))
+ {
+ symbol = null;
+ return false;
+ }
+
+ symbol = classTypeSymbol;
+ return true;
+ }
+
+ private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
+
+ public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses(
+ this IEnumerable<ClassDeclarationSyntax> source,
+ Compilation compilation
+ )
+ {
+ foreach (var cds in source)
+ {
+ if (cds.HasGenerateUnmanagedCallbacksAttribute(compilation, out var symbol))
+ yield return (cds, symbol!);
+ }
+ }
+
+ public static bool IsNested(this TypeDeclarationSyntax cds)
+ => cds.Parent is TypeDeclarationSyntax;
+
+ public static bool IsPartial(this TypeDeclarationSyntax cds)
+ => cds.Modifiers.Any(SyntaxKind.PartialKeyword);
+
+ public static bool AreAllOuterTypesPartial(
+ this TypeDeclarationSyntax cds,
+ out TypeDeclarationSyntax? typeMissingPartial
+ )
+ {
+ SyntaxNode? outerSyntaxNode = cds.Parent;
+
+ while (outerSyntaxNode is TypeDeclarationSyntax outerTypeDeclSyntax)
+ {
+ if (!outerTypeDeclSyntax.IsPartial())
+ {
+ typeMissingPartial = outerTypeDeclSyntax;
+ return false;
+ }
+
+ outerSyntaxNode = outerSyntaxNode.Parent;
+ }
+
+ typeMissingPartial = null;
+ return true;
+ }
+
+ public static string GetDeclarationKeyword(this INamedTypeSymbol namedTypeSymbol)
+ {
+ string? keyword = namedTypeSymbol.DeclaringSyntaxReferences
+ .OfType<TypeDeclarationSyntax>().FirstOrDefault()?
+ .Keyword.Text;
+
+ return keyword ?? namedTypeSymbol.TypeKind switch
+ {
+ TypeKind.Interface => "interface",
+ TypeKind.Struct => "struct",
+ _ => "class"
+ };
+ }
+
+ private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
+ SymbolDisplayFormat.FullyQualifiedFormat
+ .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+ public static string FullQualifiedName(this ITypeSymbol symbol)
+ => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
+
+ public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
+ {
+ return symbol.IsGenericType ?
+ string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") :
+ symbol.Name;
+ }
+
+ public static string FullQualifiedName(this INamespaceSymbol symbol)
+ => symbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
+
+ public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
+ => qualifiedName
+ // AddSource() doesn't support angle brackets
+ .Replace("<", "(Of ")
+ .Replace(">", ")");
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs
new file mode 100644
index 0000000000..1bbb33f5a1
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs
@@ -0,0 +1,6 @@
+namespace Godot.SourceGenerators.Internal;
+
+internal static class GeneratorClasses
+{
+ public const string GenerateUnmanagedCallbacksAttr = "Godot.SourceGenerators.Internal.GenerateUnmanagedCallbacksAttribute";
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
new file mode 100644
index 0000000000..4d1a5bb76c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <LangVersion>10</LangVersion>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
new file mode 100644
index 0000000000..da578309bc
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
@@ -0,0 +1,463 @@
+using System.Text;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+[Generator]
+public class UnmanagedCallbacksGenerator : ISourceGenerator
+{
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForPostInitialization(ctx => { GenerateAttribute(ctx); });
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ INamedTypeSymbol[] unmanagedCallbacksClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectUnmanagedCallbacksClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialUnmanagedCallbacksOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialUnmanagedCallbacksClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ foreach (var symbol in unmanagedCallbacksClasses)
+ {
+ var attr = symbol.GetGenerateUnmanagedCallbacksAttribute();
+ if (attr == null || attr.ConstructorArguments.Length != 1)
+ {
+ // TODO: Report error or throw exception, this is an invalid case and should never be reached
+ System.Diagnostics.Debug.Fail("FAILED!");
+ continue;
+ }
+
+ var funcStructType = (INamedTypeSymbol?)attr.ConstructorArguments[0].Value;
+ if (funcStructType == null)
+ {
+ // TODO: Report error or throw exception, this is an invalid case and should never be reached
+ System.Diagnostics.Debug.Fail("FAILED!");
+ continue;
+ }
+
+ var data = new CallbacksData(symbol, funcStructType);
+ GenerateInteropMethodImplementations(context, data);
+ GenerateUnmanagedCallbacksStruct(context, data);
+ }
+ }
+
+ private void GenerateAttribute(GeneratorPostInitializationContext context)
+ {
+ string source = @"using System;
+
+namespace Godot.SourceGenerators.Internal
+{
+internal class GenerateUnmanagedCallbacksAttribute : Attribute
+{
+ public Type FuncStructType { get; }
+
+ public GenerateUnmanagedCallbacksAttribute(Type funcStructType)
+ {
+ FuncStructType = funcStructType;
+ }
+}
+}";
+
+ context.AddSource("GenerateUnmanagedCallbacksAttribute.generated",
+ SourceText.From(source, Encoding.UTF8));
+ }
+
+ private void GenerateInteropMethodImplementations(GeneratorExecutionContext context, CallbacksData data)
+ {
+ var symbol = data.NativeTypeSymbol;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+ bool isInnerClass = symbol.ContainingType != null;
+
+ var source = new StringBuilder();
+ var methodSource = new StringBuilder();
+ var methodCallArguments = new StringBuilder();
+ var methodSourceAfterCall = new StringBuilder();
+
+ source.Append(
+ @"using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
+
+");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append("\n{\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ source.Append($"unsafe partial class {symbol.Name}\n");
+ source.Append("{\n");
+ source.Append($" private static {data.FuncStructSymbol.FullQualifiedName()} _unmanagedCallbacks;\n\n");
+
+ foreach (var callback in data.Methods)
+ {
+ methodSource.Clear();
+ methodCallArguments.Clear();
+ methodSourceAfterCall.Clear();
+
+ source.Append(" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]\n");
+ source.Append($" {SyntaxFacts.GetText(callback.DeclaredAccessibility)} ");
+
+ if (callback.IsStatic)
+ source.Append("static ");
+
+ source.Append("partial ");
+ source.Append(callback.ReturnType.FullQualifiedName());
+ source.Append(' ');
+ source.Append(callback.Name);
+ source.Append('(');
+
+ for (int i = 0; i < callback.Parameters.Length; i++)
+ {
+ var parameter = callback.Parameters[i];
+
+ source.Append(parameter.ToDisplayString());
+ source.Append(' ');
+ source.Append(parameter.Name);
+
+ if (parameter.RefKind == RefKind.Out)
+ {
+ // Only assign default if the parameter won't be passed by-ref or copied later.
+ if (IsGodotInteropStruct(parameter.Type))
+ methodSource.Append($" {parameter.Name} = default;\n");
+ }
+
+ if (IsByRefParameter(parameter))
+ {
+ if (IsGodotInteropStruct(parameter.Type))
+ {
+ methodSource.Append(" ");
+ AppendCustomUnsafeAsPointer(methodSource, parameter, out string varName);
+ methodCallArguments.Append(varName);
+ }
+ else if (parameter.Type.IsValueType)
+ {
+ methodSource.Append(" ");
+ AppendCopyToStackAndGetPointer(methodSource, parameter, out string varName);
+ methodCallArguments.Append($"&{varName}");
+
+ if (parameter.RefKind is RefKind.Out or RefKind.Ref)
+ {
+ methodSourceAfterCall.Append($" {parameter.Name} = {varName};\n");
+ }
+ }
+ else
+ {
+ // If it's a by-ref param and we can't get the pointer
+ // just pass it by-ref and let it be pinned.
+ AppendRefKind(methodCallArguments, parameter.RefKind)
+ .Append(' ')
+ .Append(parameter.Name);
+ }
+ }
+ else
+ {
+ methodCallArguments.Append(parameter.Name);
+ }
+
+ if (i < callback.Parameters.Length - 1)
+ {
+ source.Append(", ");
+ methodCallArguments.Append(", ");
+ }
+ }
+
+ source.Append(")\n");
+ source.Append(" {\n");
+
+ source.Append(methodSource);
+ source.Append(" ");
+
+ if (!callback.ReturnsVoid)
+ {
+ if (methodSourceAfterCall.Length != 0)
+ source.Append($"{callback.ReturnType.FullQualifiedName()} ret = ");
+ else
+ source.Append("return ");
+ }
+
+ source.Append($"_unmanagedCallbacks.{callback.Name}(");
+ source.Append(methodCallArguments);
+ source.Append(");\n");
+
+ if (methodSourceAfterCall.Length != 0)
+ {
+ source.Append(methodSourceAfterCall);
+
+ if (!callback.ReturnsVoid)
+ source.Append(" return ret;\n");
+ }
+
+ source.Append(" }\n\n");
+ }
+
+ source.Append("}\n");
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ source.Append("\n}");
+
+ source.Append("\n\n#pragma warning restore CA1707\n");
+
+ context.AddSource($"{data.NativeTypeSymbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()}.generated",
+ SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private void GenerateUnmanagedCallbacksStruct(GeneratorExecutionContext context, CallbacksData data)
+ {
+ var symbol = data.FuncStructSymbol;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+ bool isInnerClass = symbol.ContainingType != null;
+
+ var source = new StringBuilder();
+
+ source.Append(
+ @"using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
+
+");
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append("\n{\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("[StructLayout(LayoutKind.Sequential)]\n");
+ source.Append($"unsafe partial struct {symbol.Name}\n{{\n");
+
+ foreach (var callback in data.Methods)
+ {
+ source.Append(" ");
+ source.Append(callback.DeclaredAccessibility == Accessibility.Public ? "public " : "internal ");
+
+ source.Append("delegate* unmanaged<");
+
+ foreach (var parameter in callback.Parameters)
+ {
+ if (IsByRefParameter(parameter))
+ {
+ if (IsGodotInteropStruct(parameter.Type) || parameter.Type.IsValueType)
+ {
+ AppendPointerType(source, parameter.Type);
+ }
+ else
+ {
+ // If it's a by-ref param and we can't get the pointer
+ // just pass it by-ref and let it be pinned.
+ AppendRefKind(source, parameter.RefKind)
+ .Append(' ')
+ .Append(parameter.Type.FullQualifiedName());
+ }
+ }
+ else
+ {
+ source.Append(parameter.Type.FullQualifiedName());
+ }
+
+ source.Append(", ");
+ }
+
+ source.Append(callback.ReturnType.FullQualifiedName());
+ source.Append($"> {callback.Name};\n");
+ }
+
+ source.Append("}\n");
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ source.Append("}\n");
+
+ source.Append("\n#pragma warning restore CA1707\n");
+
+ context.AddSource($"{symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint()}.generated",
+ SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static bool IsGodotInteropStruct(ITypeSymbol type) =>
+ GodotInteropStructs.Contains(type.FullQualifiedName());
+
+ private static bool IsByRefParameter(IParameterSymbol parameter) =>
+ parameter.RefKind is RefKind.In or RefKind.Out or RefKind.Ref;
+
+ private static StringBuilder AppendRefKind(StringBuilder source, RefKind refKind) =>
+ refKind switch
+ {
+ RefKind.In => source.Append("in"),
+ RefKind.Out => source.Append("out"),
+ RefKind.Ref => source.Append("ref"),
+ _ => source,
+ };
+
+ private static void AppendPointerType(StringBuilder source, ITypeSymbol type)
+ {
+ source.Append(type.FullQualifiedName());
+ source.Append('*');
+ }
+
+ private static void AppendCustomUnsafeAsPointer(StringBuilder source, IParameterSymbol parameter,
+ out string varName)
+ {
+ varName = $"{parameter.Name}_ptr";
+
+ AppendPointerType(source, parameter.Type);
+ source.Append(' ');
+ source.Append(varName);
+ source.Append(" = ");
+
+ source.Append('(');
+ AppendPointerType(source, parameter.Type);
+ source.Append(')');
+
+ if (parameter.RefKind == RefKind.In)
+ source.Append("CustomUnsafe.ReadOnlyRefAsPointer(in ");
+ else
+ source.Append("CustomUnsafe.AsPointer(ref ");
+
+ source.Append(parameter.Name);
+
+ source.Append(");\n");
+ }
+
+ private static void AppendCopyToStackAndGetPointer(StringBuilder source, IParameterSymbol parameter,
+ out string varName)
+ {
+ varName = $"{parameter.Name}_copy";
+
+ source.Append(parameter.Type.FullQualifiedName());
+ source.Append(' ');
+ source.Append(varName);
+ if (parameter.RefKind is RefKind.In or RefKind.Ref)
+ {
+ source.Append(" = ");
+ source.Append(parameter.Name);
+ }
+
+ source.Append(";\n");
+ }
+
+ private static readonly string[] GodotInteropStructs =
+ {
+ "Godot.NativeInterop.godot_ref",
+ "Godot.NativeInterop.godot_variant_call_error",
+ "Godot.NativeInterop.godot_variant",
+ "Godot.NativeInterop.godot_string",
+ "Godot.NativeInterop.godot_string_name",
+ "Godot.NativeInterop.godot_node_path",
+ "Godot.NativeInterop.godot_signal",
+ "Godot.NativeInterop.godot_callable",
+ "Godot.NativeInterop.godot_array",
+ "Godot.NativeInterop.godot_dictionary",
+ "Godot.NativeInterop.godot_packed_byte_array",
+ "Godot.NativeInterop.godot_packed_int32_array",
+ "Godot.NativeInterop.godot_packed_int64_array",
+ "Godot.NativeInterop.godot_packed_float32_array",
+ "Godot.NativeInterop.godot_packed_float64_array",
+ "Godot.NativeInterop.godot_packed_string_array",
+ "Godot.NativeInterop.godot_packed_vector2_array",
+ "Godot.NativeInterop.godot_packed_vector3_array",
+ "Godot.NativeInterop.godot_packed_color_array",
+ };
+}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
new file mode 100644
index 0000000000..e720d3878c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
+ <Nullable>enable</Nullable>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+
+ <!-- To generate the .runtimeconfig.json file-->
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\GodotSharp\GodotSharp.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
new file mode 100644
index 0000000000..dad7464410
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+namespace GodotPlugins
+{
+ public static class Main
+ {
+ // IMPORTANT:
+ // Keeping strong references to the AssemblyLoadContext (our PluginLoadContext) prevents
+ // it from being unloaded. To avoid issues, we wrap the reference in this class, and mark
+ // all the methods that access it as non-inlineable. This way we prevent local references
+ // (either real or introduced by the JIT) to escape the scope of these methods due to
+ // inlining, which could keep the AssemblyLoadContext alive while trying to unload.
+ private sealed class PluginLoadContextWrapper
+ {
+ private PluginLoadContext? _pluginLoadContext;
+
+ public string? AssemblyLoadedPath
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ get => _pluginLoadContext?.AssemblyLoadedPath;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static (Assembly, PluginLoadContextWrapper) CreateAndLoadFromAssemblyName(
+ AssemblyName assemblyName,
+ string pluginPath,
+ ICollection<string> sharedAssemblies,
+ AssemblyLoadContext mainLoadContext
+ )
+ {
+ var wrapper = new PluginLoadContextWrapper();
+ wrapper._pluginLoadContext = new PluginLoadContext(
+ pluginPath, sharedAssemblies, mainLoadContext);
+ var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName);
+ return (assembly, wrapper);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public WeakReference CreateWeakReference()
+ {
+ return new WeakReference(_pluginLoadContext, trackResurrection: true);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal void Unload()
+ {
+ _pluginLoadContext?.Unload();
+ _pluginLoadContext = null;
+ }
+ }
+
+ private static readonly List<AssemblyName> SharedAssemblies = new();
+ private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
+ private static Assembly? _editorApiAssembly;
+ private static PluginLoadContextWrapper? _projectLoadContext;
+
+ private static readonly AssemblyLoadContext MainLoadContext =
+ AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
+ AssemblyLoadContext.Default;
+
+ private static DllImportResolver? _dllImportResolver;
+
+ // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later.
+ [UnmanagedCallersOnly]
+ // ReSharper disable once UnusedMember.Local
+ private static unsafe godot_bool InitializeFromEngine(IntPtr godotDllHandle, godot_bool editorHint,
+ PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks,
+ IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ try
+ {
+ _dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
+
+ SharedAssemblies.Add(CoreApiAssembly.GetName());
+ NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver);
+
+ AlcReloadCfg.Configure(alcReloadEnabled: editorHint.ToBool());
+ NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+
+ if (editorHint.ToBool())
+ {
+ _editorApiAssembly = Assembly.Load("GodotSharpEditor");
+ SharedAssemblies.Add(_editorApiAssembly.GetName());
+ NativeLibrary.SetDllImportResolver(_editorApiAssembly, _dllImportResolver);
+ }
+
+ *pluginsCallbacks = new()
+ {
+ LoadProjectAssemblyCallback = &LoadProjectAssembly,
+ LoadToolsAssemblyCallback = &LoadToolsAssembly,
+ UnloadProjectPluginCallback = &UnloadProjectPlugin,
+ };
+
+ *managedCallbacks = ManagedCallbacks.Create();
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct PluginsCallbacks
+ {
+ public unsafe delegate* unmanaged<char*, godot_string*, godot_bool> LoadProjectAssemblyCallback;
+ public unsafe delegate* unmanaged<char*, IntPtr, int, IntPtr> LoadToolsAssemblyCallback;
+ public unsafe delegate* unmanaged<godot_bool> UnloadProjectPluginCallback;
+ }
+
+ [UnmanagedCallersOnly]
+ private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath, godot_string* outLoadedAssemblyPath)
+ {
+ try
+ {
+ if (_projectLoadContext != null)
+ return godot_bool.True; // Already loaded
+
+ string assemblyPath = new(nAssemblyPath);
+
+ (var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath);
+
+ string loadedAssemblyPath = _projectLoadContext.AssemblyLoadedPath ?? assemblyPath;
+ *outLoadedAssemblyPath = Marshaling.ConvertStringToNative(loadedAssemblyPath);
+
+ ScriptManagerBridge.LookupScriptsInAssembly(projectAssembly);
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath,
+ IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ try
+ {
+ string assemblyPath = new(nAssemblyPath);
+
+ if (_editorApiAssembly == null)
+ throw new InvalidOperationException("The Godot editor API assembly is not loaded");
+
+ var (assembly, _) = LoadPlugin(assemblyPath);
+
+ NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
+
+ var method = assembly.GetType("GodotTools.GodotSharpEditor")?
+ .GetMethod("InternalCreateInstance",
+ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (method == null)
+ {
+ throw new MissingMethodException("GodotTools.GodotSharpEditor",
+ "InternalCreateInstance");
+ }
+
+ return (IntPtr?)method
+ .Invoke(null, new object[] { unmanagedCallbacks, unmanagedCallbacksSize })
+ ?? IntPtr.Zero;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return IntPtr.Zero;
+ }
+ }
+
+ private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath)
+ {
+ string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
+
+ var sharedAssemblies = new List<string>();
+
+ foreach (var sharedAssembly in SharedAssemblies)
+ {
+ string? sharedAssemblyName = sharedAssembly.Name;
+ if (sharedAssemblyName != null)
+ sharedAssemblies.Add(sharedAssemblyName);
+ }
+
+ return PluginLoadContextWrapper.CreateAndLoadFromAssemblyName(
+ new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext);
+ }
+
+ [UnmanagedCallersOnly]
+ private static godot_bool UnloadProjectPlugin()
+ {
+ try
+ {
+ return UnloadPlugin(ref _projectLoadContext).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ private static bool UnloadPlugin(ref PluginLoadContextWrapper? pluginLoadContext)
+ {
+ try
+ {
+ if (pluginLoadContext == null)
+ return true;
+
+ Console.WriteLine("Unloading assembly load context...");
+
+ var alcWeakReference = pluginLoadContext.CreateWeakReference();
+
+ pluginLoadContext.Unload();
+ pluginLoadContext = null;
+
+ int startTimeMs = Environment.TickCount;
+ bool takingTooLong = false;
+
+ while (alcWeakReference.IsAlive)
+ {
+ GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
+ GC.WaitForPendingFinalizers();
+
+ if (!alcWeakReference.IsAlive)
+ break;
+
+ int elapsedTimeMs = Environment.TickCount - startTimeMs;
+
+ if (!takingTooLong && elapsedTimeMs >= 2000)
+ {
+ takingTooLong = true;
+
+ // TODO: How to log from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine("Assembly unloading is taking longer than expected...");
+ }
+ else if (elapsedTimeMs >= 5000)
+ {
+ // TODO: How to log from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine(
+ "Failed to unload assemblies. Possible causes: Strong GC handles, running threads, etc.");
+
+ return false;
+ }
+ }
+
+ Console.WriteLine("Assembly load context unloaded successfully.");
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ // TODO: How to log exceptions from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine(e);
+ return false;
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
new file mode 100644
index 0000000000..dcd572c65e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace GodotPlugins
+{
+ public class PluginLoadContext : AssemblyLoadContext
+ {
+ private readonly AssemblyDependencyResolver _resolver;
+ private readonly ICollection<string> _sharedAssemblies;
+ private readonly AssemblyLoadContext _mainLoadContext;
+
+ public string? AssemblyLoadedPath { get; private set; }
+
+ public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies,
+ AssemblyLoadContext mainLoadContext)
+ : base(isCollectible: true)
+ {
+ _resolver = new AssemblyDependencyResolver(pluginPath);
+ _sharedAssemblies = sharedAssemblies;
+ _mainLoadContext = mainLoadContext;
+ }
+
+ protected override Assembly? Load(AssemblyName assemblyName)
+ {
+ if (assemblyName.Name == null)
+ return null;
+
+ if (_sharedAssemblies.Contains(assemblyName.Name))
+ return _mainLoadContext.LoadFromAssemblyName(assemblyName);
+
+ string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath != null)
+ {
+ AssemblyLoadedPath = assemblyPath;
+
+ // Load in memory to prevent locking the file
+ using var assemblyFile = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb");
+
+ if (File.Exists(pdbPath))
+ {
+ using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ return LoadFromStream(assemblyFile, pdbFile);
+ }
+
+ return LoadFromStream(assemblyFile);
+ }
+
+ return null;
+ }
+
+ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
+ {
+ string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
+ if (libraryPath != null)
+ return LoadUnmanagedDllFromPath(libraryPath);
+
+ return IntPtr.Zero;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln
index 4896d0a07d..8db42c2d1a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp.sln
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln
@@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\Go
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotPlugins", "GodotPlugins\GodotPlugins.csproj", "{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{7749662B-E30C-419A-B745-13852573360A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +22,13 @@ Global
{8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.Build.0 = Release|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
new file mode 100644
index 0000000000..ba65b61e95
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
@@ -0,0 +1,8 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GC/@EntryIndexedValue">GC</s:String>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=alcs/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=gdnative/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=godotsharp/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=icall/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=quat/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=vcall/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index 850ae7fc3b..f87f37bc43 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index a412047196..f2984bd1fb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -1,47 +1,35 @@
using System;
using System.Collections.Generic;
using System.Collections;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot.Collections
{
- internal class ArraySafeHandle : SafeHandle
- {
- public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
- {
- this.handle = handle;
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- protected override bool ReleaseHandle()
- {
- Array.godot_icall_Array_Dtor(handle);
- return true;
- }
- }
-
/// <summary>
/// Wrapper around Godot's Array class, an array of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine. Otherwise prefer .NET collections
/// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
/// </summary>
- public class Array : IList, IDisposable
+ public sealed class Array :
+ IList<Variant>,
+ IReadOnlyList<Variant>,
+ ICollection,
+ IDisposable
{
- private ArraySafeHandle _safeHandle;
- private bool _disposed = false;
+ internal godot_array.movable NativeValue;
+
+ private WeakReference<IDisposable> _weakReferenceToSelf;
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
/// </summary>
public Array()
{
- _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
/// <summary>
@@ -49,12 +37,12 @@ namespace Godot.Collections
/// </summary>
/// <param name="collection">The collection of elements to construct from.</param>
/// <returns>A new Godot Array.</returns>
- public Array(IEnumerable collection) : this()
+ public Array(IEnumerable<Variant> collection) : this()
{
if (collection == null)
- throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+ throw new ArgumentNullException(nameof(collection));
- foreach (object element in collection)
+ foreach (Variant element in collection)
Add(element);
}
@@ -63,31 +51,126 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The objects to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
- public Array(params object[] array) : this()
+ public Array(Variant[] array) : this()
{
if (array == null)
- {
- throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
- }
- _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
}
- internal Array(ArraySafeHandle handle)
+ public Array(Span<StringName> array) : this()
{
- _safeHandle = handle;
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
}
- internal Array(IntPtr handle)
+ public Array(Span<NodePath> array) : this()
{
- _safeHandle = new ArraySafeHandle(handle);
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ public Array(Span<RID> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ // We must use ReadOnlySpan instead of Span here as this can accept implicit conversions
+ // from derived types (e.g.: Node[]). Implicit conversion from Derived[] to Base[] are
+ // fine as long as the array is not mutated. However, Span does this type checking at
+ // instantiation, so it's not possible to use it even when not mutating anything.
+ // ReSharper disable once RedundantNameQualifier
+ public Array(ReadOnlySpan<Godot.Object> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
}
- internal IntPtr GetPtr()
+ private Array(godot_array nativeValueToOwn)
{
- if (_disposed)
- throw new ObjectDisposedException(GetType().FullName);
+ NativeValue = (godot_array.movable)(nativeValueToOwn.IsAllocated ?
+ nativeValueToOwn :
+ NativeFuncs.godotsharp_array_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
+
+ // Explicit name to make it very clear
+ internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
+ => new Array(nativeValueToOwn);
- return _safeHandle.DangerousGetHandle();
+ ~Array()
+ {
+ Dispose(false);
+ }
+
+ /// <summary>
+ /// Disposes of this <see cref="Array"/>.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public void Dispose(bool disposing)
+ {
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
+ {
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+ }
}
/// <summary>
@@ -97,7 +180,10 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array Duplicate(bool deep = false)
{
- return new Array(godot_icall_Array_Duplicate(GetPtr(), deep));
+ godot_array newArray;
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_duplicate(ref self, deep.ToGodotBool(), out newArray);
+ return CreateTakingOwnershipOfDisposableValue(newArray);
}
/// <summary>
@@ -107,7 +193,8 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
- return godot_icall_Array_Resize(GetPtr(), newSize);
+ var self = (godot_array)NativeValue;
+ return NativeFuncs.godotsharp_array_resize(ref self, newSize);
}
/// <summary>
@@ -115,7 +202,8 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
- godot_icall_Array_Shuffle(GetPtr());
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_shuffle(ref self);
}
/// <summary>
@@ -126,94 +214,136 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array operator +(Array left, Array right)
{
- return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr()));
- }
-
- // IDisposable
-
- /// <summary>
- /// Disposes of this <see cref="Array"/>.
- /// </summary>
- public void Dispose()
- {
- if (_disposed)
- return;
-
- if (_safeHandle != null)
+ if (left == null)
{
- _safeHandle.Dispose();
- _safeHandle = null;
+ if (right == null)
+ return new Array();
+
+ return right.Duplicate(deep: false);
}
- _disposed = true;
- }
+ if (right == null)
+ return left.Duplicate(deep: false);
+
+ int leftCount = left.Count;
+ int rightCount = right.Count;
- // IList
+ Array newArray = left.Duplicate(deep: false);
+ newArray.Resize(leftCount + rightCount);
- bool IList.IsReadOnly => false;
+ for (int i = 0; i < rightCount; i++)
+ newArray[i + leftCount] = right[i];
- bool IList.IsFixedSize => false;
+ return newArray;
+ }
/// <summary>
- /// Returns the object at the given <paramref name="index"/>.
+ /// Returns the item at the given <paramref name="index"/>.
/// </summary>
- /// <value>The object at the given <paramref name="index"/>.</value>
- public object this[int index]
+ /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value>
+ public unsafe Variant this[int index]
{
- get => godot_icall_Array_At(GetPtr(), index);
- set => godot_icall_Array_SetAt(GetPtr(), index, value);
+ get
+ {
+ GetVariantBorrowElementAt(index, out godot_variant borrowElem);
+ return Variant.CreateCopyingBorrowed(borrowElem);
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ var self = (godot_array)NativeValue;
+ godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
+ godot_variant* itemPtr = &ptrw[index];
+ (*itemPtr).Dispose();
+ *itemPtr = value.CopyNativeVariant();
+ }
}
/// <summary>
- /// Adds an object to the end of this <see cref="Array"/>.
+ /// Adds an item to the end of this <see cref="Array"/>.
/// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
/// </summary>
- /// <param name="value">The object to add.</param>
- /// <returns>The new size after adding the object.</returns>
- public int Add(object value) => godot_icall_Array_Add(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to add.</param>
+ public void Add(Variant item)
+ {
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
+ }
/// <summary>
- /// Checks if this <see cref="Array"/> contains the given object.
+ /// Checks if this <see cref="Array"/> contains the given item.
/// </summary>
- /// <param name="value">The item to look for.</param>
- /// <returns>Whether or not this array contains the given object.</returns>
- public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to look for.</param>
+ /// <returns>Whether or not this array contains the given item.</returns>
+ public bool Contains(Variant item) => IndexOf(item) != -1;
/// <summary>
/// Erases all items from this <see cref="Array"/>.
/// </summary>
- public void Clear() => godot_icall_Array_Clear(GetPtr());
+ public void Clear() => Resize(0);
/// <summary>
- /// Searches this <see cref="Array"/> for an object
+ /// Searches this <see cref="Array"/> for an item
/// and returns its index or -1 if not found.
/// </summary>
- /// <param name="value">The object to search for.</param>
- /// <returns>The index of the object, or -1 if not found.</returns>
- public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to search for.</param>
+ /// <returns>The index of the item, or -1 if not found.</returns>
+ public int IndexOf(Variant item)
+ {
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
+ }
/// <summary>
- /// Inserts a new object at a given position in the array.
+ /// Inserts a new item at a given position in the array.
/// The position must be a valid position of an existing item,
/// or the position at the end of the array.
/// Existing items will be moved to the right.
/// </summary>
/// <param name="index">The index to insert at.</param>
- /// <param name="value">The object to insert.</param>
- public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value);
+ /// <param name="item">The <see cref="Variant"/> item to insert.</param>
+ public void Insert(int index, Variant item)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
+ }
/// <summary>
- /// Removes the first occurrence of the specified <paramref name="value"/>
+ /// Removes the first occurrence of the specified <paramref name="item"/>
/// from this <see cref="Array"/>.
/// </summary>
- /// <param name="value">The value to remove.</param>
- public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value);
+ /// <param name="item">The value to remove.</param>
+ public bool Remove(Variant item)
+ {
+ int index = IndexOf(item);
+ if (index >= 0)
+ {
+ RemoveAt(index);
+ return true;
+ }
+
+ return false;
+ }
/// <summary>
/// Removes an element from this <see cref="Array"/> by index.
/// </summary>
/// <param name="index">The index of the element to remove.</param>
- public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index);
+ public void RemoveAt(int index)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_remove_at(ref self, index);
+ }
// ICollection
@@ -222,28 +352,77 @@ namespace Godot.Collections
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count => godot_icall_Array_Count(GetPtr());
-
- object ICollection.SyncRoot => this;
+ public int Count => NativeValue.DangerousSelfRef.Size;
bool ICollection.IsSynchronized => false;
+ object ICollection.SyncRoot => false;
+
+ bool ICollection<Variant>.IsReadOnly => false;
+
/// <summary>
/// Copies the elements of this <see cref="Array"/> to the given
- /// untyped C# array, starting at the given index.
+ /// <see cref="Variant"/> C# array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
- /// <param name="index">The index to start at.</param>
- public void CopyTo(System.Array array, int index)
+ /// <param name="arrayIndex">The index to start at.</param>
+ public void CopyTo(Variant[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
+
+ 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.");
+ }
+
+ unsafe
+ {
+ for (int i = 0; i < count; i++)
+ {
+ array[arrayIndex] = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
+ arrayIndex++;
+ }
+ }
+ }
+
+ void ICollection.CopyTo(System.Array array, int index)
{
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.");
+ {
+ throw new ArgumentOutOfRangeException(nameof(index),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
+
+ int count = Count;
- // Internal call may throw ArgumentException
- godot_icall_Array_CopyTo(GetPtr(), array, index);
+ if (array.Length < (index + 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.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]);
+ array.SetValue(obj, index);
+ index++;
+ }
+ }
}
// IEnumerable
@@ -252,7 +431,7 @@ namespace Godot.Collections
/// Gets an enumerator for this <see cref="Array"/>.
/// </summary>
/// <returns>An enumerator.</returns>
- public IEnumerator GetEnumerator()
+ public IEnumerator<Variant> GetEnumerator()
{
int count = Count;
@@ -262,77 +441,37 @@ namespace Godot.Collections
}
}
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
/// <summary>
/// Converts this <see cref="Array"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
public override string ToString()
{
- return godot_icall_Array_ToString(GetPtr());
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_to_string(ref self, out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Ctor();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Array_At(IntPtr ptr, int index);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_Count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_Add(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Clear(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
-
- [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);
+ /// <summary>
+ /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
+ /// </summary>
+ internal void GetVariantBorrowElementAt(int index, out godot_variant elem)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ GetVariantBorrowElementAtUnchecked(index, out elem);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Array_ToString(IntPtr ptr);
+ /// <summary>
+ /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
+ /// </summary>
+ internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem)
+ {
+ elem = NativeValue.DangerousSelfRef.Elements[index];
+ }
}
/// <summary>
@@ -342,16 +481,45 @@ namespace Godot.Collections
/// such as arrays or <see cref="List{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the array.</typeparam>
- public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
+ [SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
+ [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
+ public sealed class Array<[MustBeVariant] T> :
+ IList<T>,
+ IReadOnlyList<T>,
+ ICollection<T>,
+ IEnumerable<T>
{
- private Array _objectArray;
+ // 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.
- internal static int elemTypeEncoding;
- internal static IntPtr elemTypeClass;
+ private static unsafe delegate* managed<in T, godot_variant> _convertToVariantCallback;
+ private static unsafe delegate* managed<in godot_variant, T> _convertToManagedCallback;
- static Array()
+ // ReSharper restore StaticMemberInGenericType
+
+ static unsafe Array()
{
- Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass);
+ _convertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
+ _convertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
+ }
+
+ private static unsafe void ValidateVariantConversionCallbacks()
+ {
+ if (_convertToVariantCallback == null || _convertToManagedCallback == null)
+ {
+ throw new InvalidOperationException(
+ $"The array element type is not supported for conversion to Variant: '{typeof(T).FullName}'");
+ }
+ }
+
+ private readonly Array _underlyingArray;
+
+ internal ref godot_array.movable NativeValue
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _underlyingArray.NativeValue;
}
/// <summary>
@@ -359,7 +527,9 @@ namespace Godot.Collections
/// </summary>
public Array()
{
- _objectArray = new Array();
+ ValidateVariantConversionCallbacks();
+
+ _underlyingArray = new Array();
}
/// <summary>
@@ -369,10 +539,15 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array(IEnumerable<T> collection)
{
+ ValidateVariantConversionCallbacks();
+
if (collection == null)
- throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+ throw new ArgumentNullException(nameof(collection));
+
+ _underlyingArray = new Array();
- _objectArray = new Array(collection);
+ foreach (T element in collection)
+ Add(element);
}
/// <summary>
@@ -380,13 +555,17 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The items to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
- public Array(params T[] array) : this()
+ public Array(T[] array) : this()
{
+ ValidateVariantConversionCallbacks();
+
if (array == null)
- {
- throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
- }
- _objectArray = new Array(array);
+ throw new ArgumentNullException(nameof(array));
+
+ _underlyingArray = new Array();
+
+ foreach (T element in array)
+ Add(element);
}
/// <summary>
@@ -395,23 +574,14 @@ namespace Godot.Collections
/// <param name="array">The untyped array to construct from.</param>
public Array(Array array)
{
- _objectArray = array;
- }
+ ValidateVariantConversionCallbacks();
- internal Array(IntPtr handle)
- {
- _objectArray = new Array(handle);
+ _underlyingArray = array;
}
- internal Array(ArraySafeHandle handle)
- {
- _objectArray = new Array(handle);
- }
-
- internal IntPtr GetPtr()
- {
- return _objectArray.GetPtr();
- }
+ // Explicit name to make it very clear
+ internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
+ => new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
@@ -419,7 +589,7 @@ namespace Godot.Collections
/// <param name="from">The typed array to convert.</param>
public static explicit operator Array(Array<T> from)
{
- return from._objectArray;
+ return from?._underlyingArray;
}
/// <summary>
@@ -429,7 +599,7 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array<T> Duplicate(bool deep = false)
{
- return new Array<T>(_objectArray.Duplicate(deep));
+ return new Array<T>(_underlyingArray.Duplicate(deep));
}
/// <summary>
@@ -439,7 +609,7 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
- return _objectArray.Resize(newSize);
+ return _underlyingArray.Resize(newSize);
}
/// <summary>
@@ -447,7 +617,7 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
- _objectArray.Shuffle();
+ _underlyingArray.Shuffle();
}
/// <summary>
@@ -458,7 +628,18 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array<T> operator +(Array<T> left, Array<T> right)
{
- return new Array<T>(left._objectArray + right._objectArray);
+ if (left == null)
+ {
+ if (right == null)
+ return new Array<T>();
+
+ return right.Duplicate(deep: false);
+ }
+
+ if (right == null)
+ return left.Duplicate(deep: false);
+
+ return new Array<T>(left._underlyingArray + right._underlyingArray);
}
// IList<T>
@@ -467,10 +648,23 @@ namespace Godot.Collections
/// Returns the value at the given <paramref name="index"/>.
/// </summary>
/// <value>The value at the given <paramref name="index"/>.</value>
- public T this[int index]
+ public unsafe T this[int index]
{
- get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); }
- set { _objectArray[index] = value; }
+ get
+ {
+ _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
+ return _convertToManagedCallback(borrowElem);
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ var self = (godot_array)_underlyingArray.NativeValue;
+ godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
+ godot_variant* itemPtr = &ptrw[index];
+ (*itemPtr).Dispose();
+ *itemPtr = _convertToVariantCallback(value);
+ }
}
/// <summary>
@@ -479,9 +673,11 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to search for.</param>
/// <returns>The index of the item, or -1 if not found.</returns>
- public int IndexOf(T item)
+ public unsafe int IndexOf(T item)
{
- return _objectArray.IndexOf(item);
+ using var variantValue = _convertToVariantCallback(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
}
/// <summary>
@@ -492,9 +688,14 @@ namespace Godot.Collections
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="item">The item to insert.</param>
- public void Insert(int index, T item)
+ public unsafe void Insert(int index, T item)
{
- _objectArray.Insert(index, item);
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ using var variantValue = _convertToVariantCallback(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
}
/// <summary>
@@ -503,7 +704,7 @@ namespace Godot.Collections
/// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index)
{
- _objectArray.RemoveAt(index);
+ _underlyingArray.RemoveAt(index);
}
// ICollection<T>
@@ -513,10 +714,7 @@ namespace Godot.Collections
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count
- {
- get { return _objectArray.Count; }
- }
+ public int Count => _underlyingArray.Count;
bool ICollection<T>.IsReadOnly => false;
@@ -526,9 +724,11 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to add.</param>
/// <returns>The new size after adding the item.</returns>
- public void Add(T item)
+ public unsafe void Add(T item)
{
- _objectArray.Add(item);
+ using var variantValue = _convertToVariantCallback(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
}
/// <summary>
@@ -536,7 +736,7 @@ namespace Godot.Collections
/// </summary>
public void Clear()
{
- _objectArray.Clear();
+ _underlyingArray.Clear();
}
/// <summary>
@@ -544,10 +744,7 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to look for.</param>
/// <returns>Whether or not this array contains the given item.</returns>
- public bool Contains(T item)
- {
- return _objectArray.Contains(item);
- }
+ public bool Contains(T item) => IndexOf(item) != -1;
/// <summary>
/// Copies the elements of this <see cref="Array{T}"/> to the given
@@ -561,19 +758,22 @@ 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.");
-
- // 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.
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
- int count = _objectArray.Count;
+ 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++)
{
- array[arrayIndex] = (T)this[i];
+ array[arrayIndex] = this[i];
arrayIndex++;
}
}
@@ -586,7 +786,14 @@ namespace Godot.Collections
/// <returns>A <see langword="bool"/> indicating success or failure.</returns>
public bool Remove(T item)
{
- return Array.godot_icall_Array_Remove(GetPtr(), item);
+ int index = IndexOf(item);
+ if (index >= 0)
+ {
+ RemoveAt(index);
+ return true;
+ }
+
+ return false;
}
// IEnumerable<T>
@@ -597,23 +804,26 @@ namespace Godot.Collections
/// <returns>An enumerator.</returns>
public IEnumerator<T> GetEnumerator()
{
- int count = _objectArray.Count;
+ int count = _underlyingArray.Count;
for (int i = 0; i < count; i++)
{
- yield return (T)this[i];
+ yield return this[i];
}
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Array{T}"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
- public override string ToString() => _objectArray.ToString();
+ public override string ToString() => _underlyingArray.ToString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Array<T> from) => Variant.CreateFrom(from);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Array<T>(Variant from) => from.AsGodotArray<T>();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
index 2febf37f05..b7d633517a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -1,5 +1,7 @@
using System;
+#nullable enable
+
namespace Godot
{
/// <summary>
@@ -8,25 +10,28 @@ namespace Godot
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
- private readonly bool requiresLookup;
- private readonly System.Type[] scriptTypes;
+ public bool RequiresLookup { get; }
+ public Type[]? ScriptTypes { get; }
/// <summary>
/// Constructs a new AssemblyHasScriptsAttribute instance.
/// </summary>
public AssemblyHasScriptsAttribute()
{
- requiresLookup = true;
+ RequiresLookup = true;
+ ScriptTypes = null;
}
/// <summary>
/// Constructs a new AssemblyHasScriptsAttribute instance.
/// </summary>
/// <param name="scriptTypes">The specified type(s) of scripts.</param>
- public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
+ public AssemblyHasScriptsAttribute(Type[] scriptTypes)
{
- requiresLookup = false;
- this.scriptTypes = scriptTypes;
+ RequiresLookup = false;
+ ScriptTypes = scriptTypes;
}
}
}
+
+#nullable restore
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
deleted file mode 100644
index 0b00878e8c..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// An attribute that disables Godot Generators.
- /// </summary>
- [AttributeUsage(AttributeTargets.Class)]
- public class DisableGodotGeneratorsAttribute : Attribute { }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
index 46eb128d37..3d204bdf9f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
@@ -6,7 +6,7 @@ namespace Godot
/// An attribute used to export objects.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public class ExportAttribute : Attribute
+ public sealed class ExportAttribute : Attribute
{
private PropertyHint hint;
private string hintString;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs
new file mode 100644
index 0000000000..101e56f8d3
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Define a new category for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public sealed class ExportCategoryAttribute : Attribute
+ {
+ private string name;
+
+ /// <summary>
+ /// Define a new category for the following exported properties.
+ /// </summary>
+ /// <param name="name">The name of the category.</param>
+ public ExportCategoryAttribute(string name)
+ {
+ this.name = name;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs
new file mode 100644
index 0000000000..3bd532cec1
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Define a new group for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public sealed class ExportGroupAttribute : Attribute
+ {
+ private string name;
+ private string prefix;
+
+ /// <summary>
+ /// Define a new group for the following exported properties.
+ /// </summary>
+ /// <param name="name">The name of the group.</param>
+ /// <param name="prefix">If provided, the group would make group to only consider properties that have this prefix.</param>
+ public ExportGroupAttribute(string name, string prefix = "")
+ {
+ this.name = name;
+ this.prefix = prefix;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs
new file mode 100644
index 0000000000..2ae6eb0b68
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public sealed class ExportSubgroupAttribute : Attribute
+ {
+ private string name;
+ private string prefix;
+
+ /// <summary>
+ /// Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ /// <param name="name">The name of the subgroup.</param>
+ /// <param name="prefix">If provided, the subgroup would make group to only consider properties that have this prefix.</param>
+ public ExportSubgroupAttribute(string name, string prefix = "")
+ {
+ this.name = name;
+ this.prefix = prefix;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
deleted file mode 100644
index 8d4ff0fdb7..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// An attribute for a method.
- /// </summary>
- [AttributeUsage(AttributeTargets.Method)]
- internal class GodotMethodAttribute : Attribute
- {
- private string methodName;
-
- public string MethodName { get { return methodName; } }
-
- /// <summary>
- /// Constructs a new GodotMethodAttribute instance.
- /// </summary>
- /// <param name="methodName">The name of the method.</param>
- public GodotMethodAttribute(string methodName)
- {
- this.methodName = methodName;
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs
new file mode 100644
index 0000000000..23088378d1
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Attribute that restricts generic type parameters to be only types
+ /// that can be marshaled from/to a <see cref="Variant"/>.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.GenericParameter)]
+ public class MustBeVariantAttribute : Attribute { }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
index 3ebb6612de..2c8a53ae1c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -8,7 +8,7 @@ namespace Godot
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
- private string path;
+ public string Path { get; }
/// <summary>
/// Constructs a new ScriptPathAttribute instance.
@@ -16,7 +16,7 @@ namespace Godot
/// <param name="path">The file path to the script</param>
public ScriptPathAttribute(string path)
{
- this.path = path;
+ Path = path;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 437878818c..4cb9bf5758 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -167,7 +162,7 @@ namespace Godot
case 2:
return Column2;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -184,7 +179,7 @@ namespace Godot
Column2 = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -386,7 +381,7 @@ namespace Godot
case 2:
return Row2;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
@@ -413,7 +408,7 @@ namespace Godot
Row2 = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
@@ -623,41 +618,6 @@ namespace Godot
return tr;
}
- /// <summary>
- /// Returns a vector transformed (multiplied) by the basis matrix.
- /// </summary>
- /// <seealso cref="XformInv(Vector3)"/>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public Vector3 Xform(Vector3 v)
- {
- return new Vector3
- (
- Row0.Dot(v),
- Row1.Dot(v),
- Row2.Dot(v)
- );
- }
-
- /// <summary>
- /// Returns a vector transformed (multiplied) by the transposed basis matrix.
- ///
- /// Note: This results in a multiplication by the inverse of the
- /// basis matrix only if it represents a rotation-reflection.
- /// </summary>
- /// <seealso cref="Xform(Vector3)"/>
- /// <param name="v">A vector to inversely transform.</param>
- /// <returns>The inversely transformed vector.</returns>
- public Vector3 XformInv(Vector3 v)
- {
- return new Vector3
- (
- Row0[0] * v.x + Row1[0] * v.y + Row2[0] * v.z,
- Row0[1] * v.x + Row1[1] * v.y + Row2[1] * v.z,
- Row0[2] * v.x + Row1[2] * v.y + Row2[2] * v.z
- );
- }
-
private static readonly Basis[] _orthoBases = {
new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f),
new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f),
@@ -862,6 +822,41 @@ namespace Godot
}
/// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the basis matrix.
+ /// </summary>
+ /// <param name="basis">The basis matrix transformation to apply.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The transformed Vector3.</returns>
+ public static Vector3 operator *(Basis basis, Vector3 vector)
+ {
+ return new Vector3
+ (
+ basis.Row0.Dot(vector),
+ basis.Row1.Dot(vector),
+ basis.Row2.Dot(vector)
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the transposed basis matrix.
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// basis matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="vector">A Vector3 to inversely transform.</param>
+ /// <param name="basis">The basis matrix transformation to apply.</param>
+ /// <returns>The inversely transformed vector.</returns>
+ public static Vector3 operator *(Vector3 vector, Basis basis)
+ {
+ return new Vector3
+ (
+ basis.Row0[0] * vector.x + basis.Row1[0] * vector.y + basis.Row2[0] * vector.z,
+ basis.Row0[1] * vector.x + basis.Row1[1] * vector.y + basis.Row2[1] * vector.z,
+ basis.Row0[2] * vector.x + basis.Row1[2] * vector.y + basis.Row2[2] * vector.z
+ );
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the basis matrices are exactly
/// equal. Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs
new file mode 100644
index 0000000000..ac2e2fae3c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs
@@ -0,0 +1,18 @@
+namespace Godot.Bridge;
+
+public static class AlcReloadCfg
+{
+ private static bool _configured = false;
+
+ public static void Configure(bool alcReloadEnabled)
+ {
+ if (_configured)
+ return;
+
+ _configured = true;
+
+ IsAlcReloadingEnabled = alcReloadEnabled;
+ }
+
+ internal static bool IsAlcReloadingEnabled = false;
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
new file mode 100644
index 0000000000..ae44f8f4ba
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
@@ -0,0 +1,253 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ internal static class CSharpInstanceBridge
+ {
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Call(IntPtr godotObjectGCHandle, godot_string_name* method,
+ godot_variant** args, int argCount, godot_variant_call_error* refCallError, godot_variant* ret)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ {
+ *ret = default;
+ (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL;
+ return godot_bool.False;
+ }
+
+ bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method),
+ new NativeVariantPtrArgs(args),
+ argCount, out godot_variant retValue);
+
+ if (!methodInvoked)
+ {
+ *ret = default;
+ // This is important, as it tells Object::call that no method was called.
+ // Otherwise, it would prevent Object::call from calling native methods.
+ (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD;
+ return godot_bool.False;
+ }
+
+ *ret = retValue;
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *ret = default;
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ throw new InvalidOperationException();
+
+ if (godotObject.SetGodotClassPropertyValue(CustomUnsafe.AsRef(name), CustomUnsafe.AsRef(value)))
+ {
+ return godot_bool.True;
+ }
+
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
+ Variant valueManaged = Variant.CreateCopyingBorrowed(*value);
+
+ return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name,
+ godot_variant* outRet)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ throw new InvalidOperationException();
+
+ if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue))
+ {
+ *outRet = outRetValue;
+ return godot_bool.True;
+ }
+
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
+ Variant ret = godotObject._Get(nameManaged);
+
+ if (ret.VariantType == Variant.Type.Nil)
+ {
+ *outRet = default;
+ return godot_bool.False;
+ }
+
+ *outRet = Marshaling.ConvertManagedObjectToVariant(ret);
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRet = default;
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (okIfNull.ToBool())
+ godotObject?.Dispose();
+ else
+ godotObject!.Dispose();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid)
+ {
+ try
+ {
+ var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (self == null)
+ {
+ *outRes = default;
+ *outValid = godot_bool.False;
+ return;
+ }
+
+ var resultStr = self.ToString();
+
+ if (resultStr == null)
+ {
+ *outRes = default;
+ *outValid = godot_bool.False;
+ return;
+ }
+
+ *outRes = Marshaling.ConvertStringToNative(resultStr);
+ *outValid = godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRes = default;
+ *outValid = godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool HasMethodUnknownParams(IntPtr godotObjectGCHandle, godot_string_name* method)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return godot_bool.False;
+
+ return godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(method)).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void SerializeState(
+ IntPtr godotObjectGCHandle,
+ godot_dictionary* propertiesState,
+ godot_dictionary* signalEventsState
+ )
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return;
+
+ // Call OnBeforeSerialize
+
+ // ReSharper disable once SuspiciousTypeConversion.Global
+ if (godotObject is ISerializationListener serializationListener)
+ serializationListener.OnBeforeSerialize();
+
+ // Save instance state
+
+ using var info = GodotSerializationInfo.CreateCopyingBorrowed(
+ *propertiesState, *signalEventsState);
+
+ godotObject.SaveGodotObjectData(info);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void DeserializeState(
+ IntPtr godotObjectGCHandle,
+ godot_dictionary* propertiesState,
+ godot_dictionary* signalEventsState
+ )
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return;
+
+ // Restore instance state
+
+ using var info = GodotSerializationInfo.CreateCopyingBorrowed(
+ *propertiesState, *signalEventsState);
+
+ godotObject.RestoreGodotObjectData(info);
+
+ // Call OnAfterDeserialize
+
+ // ReSharper disable once SuspiciousTypeConversion.Global
+ if (godotObject is ISerializationListener serializationListener)
+ serializationListener.OnAfterDeserialize();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
new file mode 100644
index 0000000000..456a118b90
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ internal static class GCHandleBridge
+ {
+ [UnmanagedCallersOnly]
+ internal static void FreeGCHandle(IntPtr gcHandlePtr)
+ {
+ try
+ {
+ CustomGCHandle.Free(GCHandle.FromIntPtr(gcHandlePtr));
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs
new file mode 100644
index 0000000000..6d20f95007
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge;
+
+public sealed class GodotSerializationInfo : IDisposable
+{
+ private readonly Collections.Dictionary _properties;
+ private readonly Collections.Dictionary _signalEvents;
+
+ public void Dispose()
+ {
+ _properties?.Dispose();
+ _signalEvents?.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ private GodotSerializationInfo(in godot_dictionary properties, in godot_dictionary signalEvents)
+ {
+ _properties = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(properties);
+ _signalEvents = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(signalEvents);
+ }
+
+ internal static GodotSerializationInfo CreateCopyingBorrowed(
+ in godot_dictionary properties, in godot_dictionary signalEvents)
+ {
+ return new(NativeFuncs.godotsharp_dictionary_new_copy(properties),
+ NativeFuncs.godotsharp_dictionary_new_copy(signalEvents));
+ }
+
+ public void AddProperty(StringName name, Variant value)
+ {
+ _properties[name] = value;
+ }
+
+ public bool TryGetProperty(StringName name, out Variant value)
+ {
+ return _properties.TryGetValue(name, out value);
+ }
+
+ public void AddSignalEventDelegate(StringName name, Delegate eventDelegate)
+ {
+ var serializedData = new Collections.Array();
+
+ if (DelegateUtils.TrySerializeDelegate(eventDelegate, serializedData))
+ {
+ _signalEvents[name] = serializedData;
+ }
+ else if (OS.IsStdoutVerbose())
+ {
+ Console.WriteLine($"Failed to serialize event signal delegate: {name}");
+ }
+ }
+
+ public bool TryGetSignalEventDelegate<T>(StringName name, [MaybeNullWhen(false)] out T value)
+ where T : Delegate
+ {
+ if (_signalEvents.TryGetValue(name, out Variant serializedData))
+ {
+ if (DelegateUtils.TryDeserializeDelegate(serializedData.AsGodotArray(), out var eventDelegate))
+ {
+ value = eventDelegate as T;
+
+ if (value == null)
+ {
+ Console.WriteLine($"Cannot cast the deserialized event signal delegate: {name}. " +
+ $"Expected '{typeof(T).FullName}'; got '{eventDelegate.GetType().FullName}'.");
+ return false;
+ }
+
+ return true;
+ }
+ else if (OS.IsStdoutVerbose())
+ {
+ Console.WriteLine($"Failed to deserialize event signal delegate: {name}");
+ }
+
+ value = null;
+ return false;
+ }
+
+ value = null;
+ return false;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
new file mode 100644
index 0000000000..57240624bc
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct ManagedCallbacks
+ {
+ // @formatter:off
+ public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback;
+ public delegate* unmanaged<IntPtr, godot_variant**, uint, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
+ public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
+ public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
+ public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
+ public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
+ public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
+ public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
+ public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge;
+ public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
+ public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
+ public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
+ public delegate* unmanaged<IntPtr, godot_bool*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
+ public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
+ public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
+ public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_variant_call_error*, godot_variant*, godot_bool> CSharpInstanceBridge_Call;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Set;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Get;
+ public delegate* unmanaged<IntPtr, godot_bool, void> CSharpInstanceBridge_CallDispose;
+ public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, void> CSharpInstanceBridge_CallToString;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_bool> CSharpInstanceBridge_HasMethodUnknownParams;
+ public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_SerializeState;
+ public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_DeserializeState;
+ public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle;
+ public delegate* unmanaged<void*, void> DebuggingUtils_GetCurrentStackInfo;
+ public delegate* unmanaged<void> DisposablesTracker_OnGodotShuttingDown;
+ public delegate* unmanaged<godot_bool, void> GD_OnCoreApiAssemblyLoaded;
+ // @formatter:on
+
+ public static ManagedCallbacks Create()
+ {
+ return new()
+ {
+ // @formatter:off
+ SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback,
+ DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs,
+ DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals,
+ DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle,
+ DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle,
+ ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
+ ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding,
+ ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance,
+ ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
+ ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
+ ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
+ ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
+ ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge,
+ ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath,
+ ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge,
+ ScriptManagerBridge_TryReloadRegisteredScriptWithClass = &ScriptManagerBridge.TryReloadRegisteredScriptWithClass,
+ ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo,
+ ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType,
+ ScriptManagerBridge_GetPropertyInfoList = &ScriptManagerBridge.GetPropertyInfoList,
+ ScriptManagerBridge_GetPropertyDefaultValues = &ScriptManagerBridge.GetPropertyDefaultValues,
+ CSharpInstanceBridge_Call = &CSharpInstanceBridge.Call,
+ CSharpInstanceBridge_Set = &CSharpInstanceBridge.Set,
+ CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get,
+ CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose,
+ CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString,
+ CSharpInstanceBridge_HasMethodUnknownParams = &CSharpInstanceBridge.HasMethodUnknownParams,
+ CSharpInstanceBridge_SerializeState = &CSharpInstanceBridge.SerializeState,
+ CSharpInstanceBridge_DeserializeState = &CSharpInstanceBridge.DeserializeState,
+ GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle,
+ DebuggingUtils_GetCurrentStackInfo = &DebuggingUtils.GetCurrentStackInfo,
+ DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown,
+ GD_OnCoreApiAssemblyLoaded = &GD.OnCoreApiAssemblyLoaded,
+ // @formatter:on
+ };
+ }
+
+ public static void Create(IntPtr outManagedCallbacks)
+ => *(ManagedCallbacks*)outManagedCallbacks = Create();
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs
new file mode 100644
index 0000000000..647ae436ff
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Godot.Bridge;
+
+#nullable enable
+
+public struct MethodInfo
+{
+ public StringName Name { get; init; }
+ public PropertyInfo ReturnVal { get; init; }
+ public MethodFlags Flags { get; init; }
+ public int Id { get; init; } = 0;
+ public List<PropertyInfo>? Arguments { get; init; }
+ public List<Variant>? DefaultArguments { get; init; }
+
+ public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags,
+ List<PropertyInfo>? arguments, List<Variant>? defaultArguments)
+ {
+ Name = name;
+ ReturnVal = returnVal;
+ Flags = flags;
+ Arguments = arguments;
+ DefaultArguments = defaultArguments;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs
new file mode 100644
index 0000000000..80d6f7b4a5
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs
@@ -0,0 +1,24 @@
+namespace Godot.Bridge;
+
+#nullable enable
+
+public struct PropertyInfo
+{
+ public Variant.Type Type { get; init; }
+ public StringName Name { get; init; }
+ public PropertyHint Hint { get; init; }
+ public string HintString { get; init; }
+ public PropertyUsageFlags Usage { get; init; }
+ public bool Exported { get; init; }
+
+ public PropertyInfo(Variant.Type type, StringName name, PropertyHint hint, string hintString,
+ PropertyUsageFlags usage, bool exported)
+ {
+ Type = type;
+ Name = name;
+ Hint = hint;
+ HintString = hintString;
+ Usage = usage;
+ Exported = exported;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
new file mode 100644
index 0000000000..0dc5ba7678
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -0,0 +1,1028 @@
+#nullable enable
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using System.Runtime.Serialization;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ // TODO: Make class internal once we replace LookupScriptsInAssembly (the only public member) with source generators
+ public static partial class ScriptManagerBridge
+ {
+ private static ConcurrentDictionary<AssemblyLoadContext, ConcurrentDictionary<Type, byte>>
+ _alcData = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OnAlcUnloading(AssemblyLoadContext alc)
+ {
+ if (_alcData.TryRemove(alc, out var typesInAlc))
+ {
+ foreach (var type in typesInAlc.Keys)
+ {
+ if (_scriptTypeBiMap.RemoveByScriptType(type, out IntPtr scriptPtr) &&
+ !_pathTypeBiMap.TryGetScriptPath(type, out _))
+ {
+ // For scripts without a path, we need to keep the class qualified name for reloading
+ _scriptDataForReload.TryAdd(scriptPtr,
+ (type.Assembly.GetName().Name, type.FullName ?? type.ToString()));
+ }
+
+ _pathTypeBiMap.RemoveByScriptType(type);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void AddTypeForAlcReloading(Type type)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(type.Assembly);
+ if (alc == null)
+ return;
+
+ var typesInAlc = _alcData.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ typesInAlc.TryAdd(type, 0);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void TrackAlcForUnloading(AssemblyLoadContext alc)
+ {
+ _ = _alcData.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ }
+
+ private static ScriptTypeBiMap _scriptTypeBiMap = new();
+ private static PathScriptTypeBiMap _pathTypeBiMap = new();
+
+ private static ConcurrentDictionary<IntPtr, (string? assemblyName, string classFullName)>
+ _scriptDataForReload = new();
+
+ [UnmanagedCallersOnly]
+ internal static void FrameCallback()
+ {
+ try
+ {
+ Dispatcher.DefaultGodotTaskScheduler?.Activate();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
+ IntPtr godotObject)
+ {
+ // TODO: Optimize with source generators and delegate pointers
+
+ try
+ {
+ using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(nativeTypeName)));
+ string nativeTypeNameStr = stringName.ToString();
+
+ Type nativeType = TypeGetProxyClass(nativeTypeNameStr) ?? throw new InvalidOperationException(
+ "Wrapper class not found for type: " + nativeTypeNameStr);
+ var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
+
+ var ctor = nativeType.GetConstructor(
+ BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
+ null, Type.EmptyTypes, null);
+
+ obj.NativePtr = godotObject;
+
+ _ = ctor!.Invoke(obj, null);
+
+ return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(obj));
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return IntPtr.Zero;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr,
+ IntPtr godotObject,
+ godot_variant** args, int argCount)
+ {
+ // TODO: Optimize with source generators and delegate pointers
+
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ var obj = (Object)FormatterServices.GetUninitializedObject(scriptType);
+
+ var ctor = scriptType
+ .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Where(c => c.GetParameters().Length == argCount)
+ .FirstOrDefault();
+
+ if (ctor == null)
+ {
+ if (argCount == 0)
+ {
+ throw new MissingMemberException(
+ $"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor.");
+ }
+ else
+ {
+ throw new MissingMemberException(
+ $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters.");
+ }
+ }
+
+ var parameters = ctor.GetParameters();
+ int paramCount = parameters.Length;
+
+ var invokeParams = new object?[paramCount];
+
+ for (int i = 0; i < paramCount; i++)
+ {
+ invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType(
+ *args[i], parameters[i].ParameterType);
+ }
+
+ obj.NativePtr = godotObject;
+
+ _ = ctor.Invoke(obj, invokeParams);
+
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* outRes)
+ {
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType))
+ {
+ *outRes = default;
+ return;
+ }
+
+ var native = Object.InternalGetClassNativeBase(scriptType);
+
+ var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.Public | BindingFlags.NonPublic);
+
+ if (field == null)
+ {
+ *outRes = default;
+ return;
+ }
+
+ var nativeName = (StringName?)field.GetValue(null);
+
+ if (nativeName == null)
+ {
+ *outRes = default;
+ return;
+ }
+
+ *outRes = NativeFuncs.godotsharp_string_name_new_copy((godot_string_name)nativeName.NativeValue);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRes = default;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
+ {
+ try
+ {
+ var target = (Object?)GCHandle.FromIntPtr(gcHandlePtr).Target;
+ if (target != null)
+ target.NativePtr = newPtr;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static Type? TypeGetProxyClass(string nativeTypeNameStr)
+ {
+ // Performance is not critical here as this will be replaced with a generated dictionary.
+
+ if (nativeTypeNameStr[0] == '_')
+ nativeTypeNameStr = nativeTypeNameStr.Substring(1);
+
+ Type? wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr);
+
+ if (wrapperType == null)
+ {
+ wrapperType = AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor")?
+ .GetType("Godot." + nativeTypeNameStr);
+ }
+
+ static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed;
+
+ if (wrapperType != null && IsStatic(wrapperType))
+ {
+ // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
+ return typeof(Object);
+ }
+
+ return wrapperType;
+ }
+
+ // Called from GodotPlugins
+ // ReSharper disable once UnusedMember.Local
+ public static void LookupScriptsInAssembly(Assembly assembly)
+ {
+ static void LookupScriptForClass(Type type)
+ {
+ var scriptPathAttr = type.GetCustomAttributes(inherit: false)
+ .OfType<ScriptPathAttribute>()
+ .FirstOrDefault();
+
+ if (scriptPathAttr == null)
+ return;
+
+ _pathTypeBiMap.Add(scriptPathAttr.Path, type);
+
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ AddTypeForAlcReloading(type);
+ }
+ }
+
+ var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false)
+ .OfType<AssemblyHasScriptsAttribute>()
+ .FirstOrDefault();
+
+ if (assemblyHasScriptsAttr == null)
+ return;
+
+ if (assemblyHasScriptsAttr.RequiresLookup)
+ {
+ // 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.
+
+ var typeOfGodotObject = typeof(Object);
+
+ foreach (var type in assembly.GetTypes())
+ {
+ if (type.IsNested)
+ continue;
+
+ if (!typeOfGodotObject.IsAssignableFrom(type))
+ continue;
+
+ LookupScriptForClass(type);
+ }
+ }
+ else
+ {
+ // This is the most likely scenario as we use C# source generators
+
+ var scriptTypes = assemblyHasScriptsAttr.ScriptTypes;
+
+ if (scriptTypes != null)
+ {
+ for (int i = 0; i < scriptTypes.Length; i++)
+ {
+ LookupScriptForClass(scriptTypes[i]);
+ }
+ }
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr,
+ godot_string_name* eventSignalName, godot_variant** args, int argCount, godot_bool* outOwnerIsNull)
+ {
+ try
+ {
+ var owner = (Object?)GCHandle.FromIntPtr(ownerGCHandlePtr).Target;
+
+ if (owner == null)
+ {
+ *outOwnerIsNull = godot_bool.True;
+ return;
+ }
+
+ *outOwnerIsNull = godot_bool.False;
+
+ owner.RaiseGodotClassSignalCallbacks(CustomUnsafe.AsRef(eventSignalName),
+ new NativeVariantPtrArgs(args), argCount);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outOwnerIsNull = godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
+ {
+ try
+ {
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType))
+ return godot_bool.False;
+
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtrMaybeBase, out Type? maybeBaseType))
+ return godot_bool.False;
+
+ return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (!_scriptTypeBiMap.IsScriptRegistered(scriptPtr))
+ {
+ string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
+
+ if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
+ return godot_bool.False;
+
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+ }
+ }
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetOrCreateScriptBridgeForPath(godot_string* scriptPath, godot_ref* outScript)
+ {
+ string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
+
+ if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
+ {
+ NativeFuncs.godotsharp_internal_new_csharp_script(outScript);
+ return;
+ }
+
+ GetOrCreateScriptBridgeForType(scriptType, outScript);
+ }
+
+ private static unsafe void GetOrCreateScriptBridgeForType(Type scriptType, godot_ref* outScript)
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptPtr(scriptType, out IntPtr scriptPtr))
+ {
+ // Use existing
+ NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr);
+ return;
+ }
+
+ // This path is slower, but it's only executed for the first instantiation of the type
+ CreateScriptBridgeForType(scriptType, outScript);
+ }
+ }
+
+ internal static unsafe void GetOrLoadOrCreateScriptForType(Type scriptType, godot_ref* outScript)
+ {
+ static bool GetPathOtherwiseGetOrCreateScript(Type scriptType, godot_ref* outScript,
+ [MaybeNullWhen(false)] out string scriptPath)
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptPtr(scriptType, out IntPtr scriptPtr))
+ {
+ // Use existing
+ NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr);
+ scriptPath = null;
+ return false;
+ }
+
+ // This path is slower, but it's only executed for the first instantiation of the type
+
+ if (_pathTypeBiMap.TryGetScriptPath(scriptType, out scriptPath))
+ return true;
+
+ CreateScriptBridgeForType(scriptType, outScript);
+ scriptPath = null;
+ return false;
+ }
+ }
+
+ if (GetPathOtherwiseGetOrCreateScript(scriptType, outScript, out string? scriptPath))
+ {
+ // This path is slower, but it's only executed for the first instantiation of the type
+
+ // This must be done outside the read-write lock, as the script resource loading can lock it
+ using godot_string scriptPathIn = Marshaling.ConvertStringToNative(scriptPath);
+ if (!NativeFuncs.godotsharp_internal_script_load(scriptPathIn, outScript).ToBool())
+ {
+ GD.PushError($"Cannot load script for type '{scriptType.FullName}'. Path: '{scriptPath}'.");
+
+ // If loading of the script fails, best we can do create a new script
+ // with no path, as we do for types without an associated script file.
+ GetOrCreateScriptBridgeForType(scriptType, outScript);
+ }
+ }
+ }
+
+ private static unsafe void CreateScriptBridgeForType(Type scriptType, godot_ref* outScript)
+ {
+ NativeFuncs.godotsharp_internal_new_csharp_script(outScript);
+ IntPtr scriptPtr = outScript->Reference;
+
+ // Caller takes care of locking
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void RemoveScriptBridge(IntPtr scriptPtr)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ _scriptTypeBiMap.Remove(scriptPtr);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static godot_bool TryReloadRegisteredScriptWithClass(IntPtr scriptPtr)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptType(scriptPtr, out _))
+ {
+ // NOTE:
+ // Currently, we reload all scripts, not only the ones from the unloaded ALC.
+ // As such, we need to handle this case instead of treating it as an error.
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+ return godot_bool.True;
+ }
+
+ if (!_scriptDataForReload.TryGetValue(scriptPtr, out var dataForReload))
+ {
+ GD.PushError("Missing class qualified name for reloading script");
+ return godot_bool.False;
+ }
+
+ _ = _scriptDataForReload.TryRemove(scriptPtr, out _);
+
+ if (dataForReload.assemblyName == null)
+ {
+ GD.PushError(
+ $"Missing assembly name of class '{dataForReload.classFullName}' for reloading script");
+ return godot_bool.False;
+ }
+
+ var scriptType = ReflectionUtils.FindTypeInLoadedAssemblies(dataForReload.assemblyName,
+ dataForReload.classFullName);
+
+ if (scriptType == null)
+ {
+ // The class was removed, can't reload
+ return godot_bool.False;
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ if (!typeof(Godot.Object).IsAssignableFrom(scriptType))
+ {
+ // The class no longer inherits Godot.Object, can't reload
+ return godot_bool.False;
+ }
+
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+
+ return godot_bool.True;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
+ godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
+ godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
+ {
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+
+ *outTool = scriptType.GetCustomAttributes(inherit: false)
+ .OfType<ToolAttribute>()
+ .Any().ToGodotBool();
+
+ if (!(*outTool).ToBool() && scriptType.IsNested)
+ {
+ *outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
+ .OfType<ToolAttribute>()
+ .Any() ?? false).ToGodotBool();
+ }
+
+ if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
+ *outTool = godot_bool.True;
+
+ // Methods
+
+ // Performance is not critical here as this will be replaced with source generators.
+ using var methods = new Collections.Array();
+
+ Type? top = scriptType;
+ Type native = Object.InternalGetClassNativeBase(top);
+
+ while (top != null && top != native)
+ {
+ var methodList = GetMethodListForType(top);
+
+ if (methodList != null)
+ {
+ foreach (var method in methodList)
+ {
+ var methodInfo = new Collections.Dictionary();
+
+ methodInfo.Add("name", method.Name);
+
+ var methodParams = new Collections.Array();
+
+ if (method.Arguments != null)
+ {
+ foreach (var param in method.Arguments)
+ {
+ methodParams.Add(new Collections.Dictionary()
+ {
+ { "name", param.Name },
+ { "type", (int)param.Type },
+ { "usage", (int)param.Usage }
+ });
+ }
+ }
+
+ methodInfo.Add("params", methodParams);
+
+ methods.Add(methodInfo);
+ }
+ }
+
+ top = top.BaseType;
+ }
+
+ *outMethodsDest = NativeFuncs.godotsharp_array_new_copy(
+ (godot_array)methods.NativeValue);
+
+ // RPC functions
+
+ Collections.Dictionary rpcFunctions = new();
+
+ top = scriptType;
+
+ while (top != null && top != native)
+ {
+ foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
+ BindingFlags.NonPublic | BindingFlags.Public))
+ {
+ if (method.IsStatic)
+ continue;
+
+ string methodName = method.Name;
+
+ if (rpcFunctions.ContainsKey(methodName))
+ continue;
+
+ var rpcAttr = method.GetCustomAttributes(inherit: false)
+ .OfType<RPCAttribute>().FirstOrDefault();
+
+ if (rpcAttr == null)
+ continue;
+
+ var rpcConfig = new Collections.Dictionary();
+
+ rpcConfig["rpc_mode"] = (long)rpcAttr.Mode;
+ rpcConfig["call_local"] = rpcAttr.CallLocal;
+ rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode;
+ rpcConfig["channel"] = rpcAttr.TransferChannel;
+
+ rpcFunctions.Add(methodName, rpcConfig);
+ }
+
+ top = top.BaseType;
+ }
+
+ *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)(rpcFunctions).NativeValue);
+
+ // Event signals
+
+ // Performance is not critical here as this will be replaced with source generators.
+ using var signals = new Collections.Dictionary();
+
+ top = scriptType;
+
+ while (top != null && top != native)
+ {
+ var signalList = GetSignalListForType(top);
+
+ if (signalList != null)
+ {
+ foreach (var signal in signalList)
+ {
+ string signalName = signal.Name;
+
+ if (signals.ContainsKey(signalName))
+ continue;
+
+ var signalParams = new Collections.Array();
+
+ if (signal.Arguments != null)
+ {
+ foreach (var param in signal.Arguments)
+ {
+ signalParams.Add(new Collections.Dictionary()
+ {
+ { "name", param.Name },
+ { "type", (int)param.Type },
+ { "usage", (int)param.Usage }
+ });
+ }
+ }
+
+ signals.Add(signalName, signalParams);
+ }
+ }
+
+ top = top.BaseType;
+ }
+
+ *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)signals.NativeValue);
+
+ // Base script
+
+ var baseType = scriptType.BaseType;
+ if (baseType != null && baseType != native)
+ {
+ GetOrLoadOrCreateScriptForType(baseType, outBaseScript);
+ }
+ else
+ {
+ *outBaseScript = default;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outTool = godot_bool.False;
+ *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
+ *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
+ *outBaseScript = default;
+ }
+ }
+
+ private static List<MethodInfo>? GetSignalListForType(Type type)
+ {
+ var getGodotSignalListMethod = type.GetMethod(
+ "GetGodotSignalList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotSignalListMethod == null)
+ return null;
+
+ return (List<MethodInfo>?)getGodotSignalListMethod.Invoke(null, null);
+ }
+
+ private static List<MethodInfo>? GetMethodListForType(Type type)
+ {
+ var getGodotMethodListMethod = type.GetMethod(
+ "GetGodotMethodList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotMethodListMethod == null)
+ return null;
+
+ return (List<MethodInfo>?)getGodotMethodListMethod.Invoke(null, null);
+ }
+
+ // ReSharper disable once InconsistentNaming
+ [SuppressMessage("ReSharper", "NotAccessedField.Local")]
+ [StructLayout(LayoutKind.Sequential)]
+ private ref struct godotsharp_property_info
+ {
+ // Careful with padding...
+ public godot_string_name Name; // Not owned
+ public godot_string HintString;
+ public int Type;
+ public int Hint;
+ public int Usage;
+ public godot_bool Exported;
+
+ public void Dispose()
+ {
+ HintString.Dispose();
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetPropertyInfoList(IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, godot_string*, void*, int, void> addPropInfoFunc)
+ {
+ try
+ {
+ Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ GetPropertyInfoListForType(scriptType, scriptPtr, addPropInfoFunc);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static unsafe void GetPropertyInfoListForType(Type type, IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, godot_string*, void*, int, void> addPropInfoFunc)
+ {
+ try
+ {
+ var getGodotPropertyListMethod = type.GetMethod(
+ "GetGodotPropertyList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotPropertyListMethod == null)
+ return;
+
+ var properties = (List<PropertyInfo>?)
+ getGodotPropertyListMethod.Invoke(null, null);
+
+ if (properties == null || properties.Count <= 0)
+ return;
+
+ int length = properties.Count;
+
+ // There's no recursion here, so it's ok to go with a big enough number for most cases
+ // stackMaxSize = stackMaxLength * sizeof(godotsharp_property_info)
+ const int stackMaxLength = 32;
+ bool useStack = length < stackMaxLength;
+
+ godotsharp_property_info* interopProperties;
+
+ if (useStack)
+ {
+ // Weird limitation, hence the need for aux:
+ // "In the case of pointer types, you can use a stackalloc expression only in a local variable declaration to initialize the variable."
+ var aux = stackalloc godotsharp_property_info[length];
+ interopProperties = aux;
+ }
+ else
+ {
+ interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length))!;
+ }
+
+ try
+ {
+ for (int i = 0; i < length; i++)
+ {
+ var property = properties[i];
+
+ godotsharp_property_info interopProperty = new()
+ {
+ Type = (int)property.Type,
+ Name = (godot_string_name)property.Name.NativeValue, // Not owned
+ Hint = (int)property.Hint,
+ HintString = Marshaling.ConvertStringToNative(property.HintString),
+ Usage = (int)property.Usage,
+ Exported = property.Exported.ToGodotBool()
+ };
+
+ interopProperties[i] = interopProperty;
+ }
+
+ using godot_string currentClassName = Marshaling.ConvertStringToNative(type.Name);
+
+ addPropInfoFunc(scriptPtr, &currentClassName, interopProperties, length);
+
+ // We're borrowing the StringName's without making an owning copy, so the
+ // managed collection needs to be kept alive until `addPropInfoFunc` returns.
+ GC.KeepAlive(properties);
+ }
+ finally
+ {
+ for (int i = 0; i < length; i++)
+ interopProperties[i].Dispose();
+
+ if (!useStack)
+ NativeMemory.Free(interopProperties);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ // ReSharper disable once InconsistentNaming
+ [SuppressMessage("ReSharper", "NotAccessedField.Local")]
+ [StructLayout(LayoutKind.Sequential)]
+ private ref struct godotsharp_property_def_val_pair
+ {
+ // Careful with padding...
+ public godot_string_name Name; // Not owned
+ public godot_variant Value;
+
+ public void Dispose()
+ {
+ Value.Dispose();
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetPropertyDefaultValues(IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, void*, int, void> addDefValFunc)
+ {
+ try
+ {
+ Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ Type native = Object.InternalGetClassNativeBase(top);
+
+ while (top != null && top != native)
+ {
+ GetPropertyDefaultValuesForType(top, scriptPtr, addDefValFunc);
+
+ top = top.BaseType;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [SkipLocalsInit]
+ private static unsafe void GetPropertyDefaultValuesForType(Type type, IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, void*, int, void> addDefValFunc)
+ {
+ try
+ {
+ var getGodotPropertyDefaultValuesMethod = type.GetMethod(
+ "GetGodotPropertyDefaultValues",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotPropertyDefaultValuesMethod == null)
+ return;
+
+ var defaultValues = (Dictionary<StringName, object>?)
+ getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+
+ if (defaultValues == null || defaultValues.Count <= 0)
+ return;
+
+ int length = defaultValues.Count;
+
+ // There's no recursion here, so it's ok to go with a big enough number for most cases
+ // stackMaxSize = stackMaxLength * sizeof(godotsharp_property_def_val_pair)
+ const int stackMaxLength = 32;
+ bool useStack = length < stackMaxLength;
+
+ godotsharp_property_def_val_pair* interopDefaultValues;
+
+ if (useStack)
+ {
+ // Weird limitation, hence the need for aux:
+ // "In the case of pointer types, you can use a stackalloc expression only in a local variable declaration to initialize the variable."
+ var aux = stackalloc godotsharp_property_def_val_pair[length];
+ interopDefaultValues = aux;
+ }
+ else
+ {
+ interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length))!;
+ }
+
+ try
+ {
+ int i = 0;
+ foreach (var defaultValuePair in defaultValues)
+ {
+ godotsharp_property_def_val_pair interopProperty = new()
+ {
+ Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned
+ Value = Marshaling.ConvertManagedObjectToVariant(defaultValuePair.Value)
+ };
+
+ interopDefaultValues[i] = interopProperty;
+
+ i++;
+ }
+
+ addDefValFunc(scriptPtr, interopDefaultValues, length);
+
+ // We're borrowing the StringName's without making an owning copy, so the
+ // managed collection needs to be kept alive until `addDefValFunc` returns.
+ GC.KeepAlive(defaultValues);
+ }
+ finally
+ {
+ for (int i = 0; i < length; i++)
+ interopDefaultValues[i].Dispose();
+
+ if (!useStack)
+ NativeMemory.Free(interopDefaultValues);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
+ godot_bool createWeak)
+ {
+ try
+ {
+ var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
+
+ object? target = oldGCHandle.Target;
+
+ if (target == null)
+ {
+ CustomGCHandle.Free(oldGCHandle);
+ *outNewGCHandlePtr = IntPtr.Zero;
+ return godot_bool.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.
+ var newGCHandle = createWeak.ToBool() ?
+ CustomGCHandle.AllocWeak(target) :
+ CustomGCHandle.AllocStrong(target);
+
+ CustomGCHandle.Free(oldGCHandle);
+ *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outNewGCHandlePtr = IntPtr.Zero;
+ return godot_bool.False;
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
new file mode 100644
index 0000000000..a58f6849ad
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Godot.Bridge;
+
+#nullable enable
+
+public static partial class ScriptManagerBridge
+{
+ private class ScriptTypeBiMap
+ {
+ public readonly object ReadWriteLock = new();
+ private System.Collections.Generic.Dictionary<IntPtr, Type> _scriptTypeMap = new();
+ private System.Collections.Generic.Dictionary<Type, IntPtr> _typeScriptMap = new();
+
+ public void Add(IntPtr scriptPtr, Type scriptType)
+ {
+ // TODO: What if this is called while unloading a load context, but after we already did cleanup in preparation for unloading?
+
+ _scriptTypeMap.Add(scriptPtr, scriptType);
+ _typeScriptMap.Add(scriptType, scriptPtr);
+
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ AddTypeForAlcReloading(scriptType);
+ }
+ }
+
+ public void Remove(IntPtr scriptPtr)
+ {
+ if (_scriptTypeMap.Remove(scriptPtr, out Type? scriptType))
+ _ = _typeScriptMap.Remove(scriptType);
+ }
+
+ public bool RemoveByScriptType(Type scriptType, out IntPtr scriptPtr)
+ {
+ if (_typeScriptMap.Remove(scriptType, out scriptPtr))
+ return _scriptTypeMap.Remove(scriptPtr);
+ return false;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Type GetScriptType(IntPtr scriptPtr) => _scriptTypeMap[scriptPtr];
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptType(IntPtr scriptPtr, [MaybeNullWhen(false)] out Type scriptType) =>
+ _scriptTypeMap.TryGetValue(scriptPtr, out scriptType);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptPtr(Type scriptType, out IntPtr scriptPtr) =>
+ _typeScriptMap.TryGetValue(scriptType, out scriptPtr);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsScriptRegistered(IntPtr scriptPtr) => _scriptTypeMap.ContainsKey(scriptPtr);
+ }
+
+ private class PathScriptTypeBiMap
+ {
+ private System.Collections.Generic.Dictionary<string, Type> _pathTypeMap = new();
+ private System.Collections.Generic.Dictionary<Type, string> _typePathMap = new();
+
+ public void Add(string scriptPath, Type scriptType)
+ {
+ _pathTypeMap.Add(scriptPath, scriptType);
+
+ // Due to partial classes, more than one file can point to the same type, so
+ // there could be duplicate keys in this case. We only add a type as key once.
+ _typePathMap.TryAdd(scriptType, scriptPath);
+ }
+
+ public void RemoveByScriptType(Type scriptType)
+ {
+ foreach (var pair in _pathTypeMap
+ .Where(p => p.Value == scriptType).ToArray())
+ {
+ _pathTypeMap.Remove(pair.Key);
+ }
+
+ _typePathMap.Remove(scriptType);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptType(string scriptPath, [MaybeNullWhen(false)] out Type scriptType) =>
+ _pathTypeMap.TryGetValue(scriptPath, out scriptType);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptPath(Type scriptType, [MaybeNullWhen(false)] out string scriptPath) =>
+ _typePathMap.TryGetValue(scriptType, out scriptPath);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index 2722b64e6d..1b7f5158fd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -24,7 +26,7 @@ namespace Godot
/// }
/// </code>
/// </example>
- public struct Callable
+ public readonly struct Callable
{
private readonly Object _target;
private readonly StringName _method;
@@ -34,10 +36,12 @@ namespace Godot
/// Object that contains the method.
/// </summary>
public Object Target => _target;
+
/// <summary>
/// Name of the method that will be called.
/// </summary>
public StringName Method => _method;
+
/// <summary>
/// Delegate of the method that will be called.
/// </summary>
@@ -73,15 +77,43 @@ namespace Godot
_delegate = @delegate;
}
+ private const int VarArgsSpanThreshold = 5;
+
/// <summary>
/// Calls the method represented by this <see cref="Callable"/>.
/// Arguments can be passed and should match the method's signature.
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
/// <returns>The value returned by the method.</returns>
- public object Call(params object[] args)
+ public unsafe Variant Call(params Variant[] args)
{
- return godot_icall_Callable_Call(ref this, args);
+ using godot_callable callable = Marshaling.ConvertCallableToNative(this);
+
+ int argc = args.Length;
+
+ Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() :
+ new godot_variant.movable[argc];
+
+ Span<IntPtr> argsSpan = argc <= 10 ?
+ stackalloc IntPtr[argc] :
+ new IntPtr[argc];
+
+ using var variantSpanDisposer = new VariantSpanDisposer(argsStoreSpan);
+
+ fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
+ fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
+ {
+ for (int i = 0; i < argc; i++)
+ {
+ varargs[i] = (godot_variant)args[i].NativeVar;
+ argsPtr[i] = new IntPtr(&varargs[i]);
+ }
+
+ godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
+ (godot_variant**)argsPtr, argc, out _);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
+ }
}
/// <summary>
@@ -89,15 +121,33 @@ namespace Godot
/// Arguments can be passed and should match the method's signature.
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
- public void CallDeferred(params object[] args)
+ public unsafe void CallDeferred(params Variant[] args)
{
- godot_icall_Callable_CallDeferred(ref this, args);
- }
+ using godot_callable callable = Marshaling.ConvertCallableToNative(this);
+
+ int argc = args.Length;
+
+ Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc godot_variant.movable[VarArgsSpanThreshold].Cleared() :
+ new godot_variant.movable[argc];
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Callable_Call(ref Callable callable, object[] args);
+ Span<IntPtr> argsSpan = argc <= 10 ?
+ stackalloc IntPtr[argc] :
+ new IntPtr[argc];
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Callable_CallDeferred(ref Callable callable, object[] args);
+ using var variantSpanDisposer = new VariantSpanDisposer(argsStoreSpan);
+
+ fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
+ fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
+ {
+ for (int i = 0; i < argc; i++)
+ {
+ varargs[i] = (godot_variant)args[i].NativeVar;
+ argsPtr[i] = new IntPtr(&varargs[i]);
+ }
+
+ NativeFuncs.godotsharp_callable_call_deferred(callable, (godot_variant**)argsPtr, argc);
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index a6324504fc..ed0e1efd35 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -210,7 +210,7 @@ namespace Godot
case 3:
return a;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -230,7 +230,7 @@ namespace Godot
a = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -841,7 +841,7 @@ namespace Godot
return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1);
}
- private string ToHex32(float val)
+ private static string ToHex32(float val)
{
byte b = (byte)Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
return b.HexEncode();
@@ -849,7 +849,7 @@ namespace Godot
internal static bool HtmlIsValid(string color)
{
- if (color.Length == 0)
+ if (string.IsNullOrEmpty(color))
{
return false;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index 68c821b447..bacf7c89e6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -10,152 +10,152 @@ namespace Godot
{
// Color names and values are derived from core/math/color_names.inc
internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> {
- {"ALICEBLUE", new Color(0.94f, 0.97f, 1.00f)},
- {"ANTIQUEWHITE", new Color(0.98f, 0.92f, 0.84f)},
- {"AQUA", new Color(0.00f, 1.00f, 1.00f)},
- {"AQUAMARINE", new Color(0.50f, 1.00f, 0.83f)},
- {"AZURE", new Color(0.94f, 1.00f, 1.00f)},
- {"BEIGE", new Color(0.96f, 0.96f, 0.86f)},
- {"BISQUE", new Color(1.00f, 0.89f, 0.77f)},
- {"BLACK", new Color(0.00f, 0.00f, 0.00f)},
- {"BLANCHEDALMOND", new Color(1.00f, 0.92f, 0.80f)},
- {"BLUE", new Color(0.00f, 0.00f, 1.00f)},
- {"BLUEVIOLET", new Color(0.54f, 0.17f, 0.89f)},
- {"BROWN", new Color(0.65f, 0.16f, 0.16f)},
- {"BURLYWOOD", new Color(0.87f, 0.72f, 0.53f)},
- {"CADETBLUE", new Color(0.37f, 0.62f, 0.63f)},
- {"CHARTREUSE", new Color(0.50f, 1.00f, 0.00f)},
- {"CHOCOLATE", new Color(0.82f, 0.41f, 0.12f)},
- {"CORAL", new Color(1.00f, 0.50f, 0.31f)},
- {"CORNFLOWERBLUE", new Color(0.39f, 0.58f, 0.93f)},
- {"CORNSILK", new Color(1.00f, 0.97f, 0.86f)},
- {"CRIMSON", new Color(0.86f, 0.08f, 0.24f)},
- {"CYAN", new Color(0.00f, 1.00f, 1.00f)},
- {"DARKBLUE", new Color(0.00f, 0.00f, 0.55f)},
- {"DARKCYAN", new Color(0.00f, 0.55f, 0.55f)},
- {"DARKGOLDENROD", new Color(0.72f, 0.53f, 0.04f)},
- {"DARKGRAY", new Color(0.66f, 0.66f, 0.66f)},
- {"DARKGREEN", new Color(0.00f, 0.39f, 0.00f)},
- {"DARKKHAKI", new Color(0.74f, 0.72f, 0.42f)},
- {"DARKMAGENTA", new Color(0.55f, 0.00f, 0.55f)},
- {"DARKOLIVEGREEN", new Color(0.33f, 0.42f, 0.18f)},
- {"DARKORANGE", new Color(1.00f, 0.55f, 0.00f)},
- {"DARKORCHID", new Color(0.60f, 0.20f, 0.80f)},
- {"DARKRED", new Color(0.55f, 0.00f, 0.00f)},
- {"DARKSALMON", new Color(0.91f, 0.59f, 0.48f)},
- {"DARKSEAGREEN", new Color(0.56f, 0.74f, 0.56f)},
- {"DARKSLATEBLUE", new Color(0.28f, 0.24f, 0.55f)},
- {"DARKSLATEGRAY", new Color(0.18f, 0.31f, 0.31f)},
- {"DARKTURQUOISE", new Color(0.00f, 0.81f, 0.82f)},
- {"DARKVIOLET", new Color(0.58f, 0.00f, 0.83f)},
- {"DEEPPINK", new Color(1.00f, 0.08f, 0.58f)},
- {"DEEPSKYBLUE", new Color(0.00f, 0.75f, 1.00f)},
- {"DIMGRAY", new Color(0.41f, 0.41f, 0.41f)},
- {"DODGERBLUE", new Color(0.12f, 0.56f, 1.00f)},
- {"FIREBRICK", new Color(0.70f, 0.13f, 0.13f)},
- {"FLORALWHITE", new Color(1.00f, 0.98f, 0.94f)},
- {"FORESTGREEN", new Color(0.13f, 0.55f, 0.13f)},
- {"FUCHSIA", new Color(1.00f, 0.00f, 1.00f)},
- {"GAINSBORO", new Color(0.86f, 0.86f, 0.86f)},
- {"GHOSTWHITE", new Color(0.97f, 0.97f, 1.00f)},
- {"GOLD", new Color(1.00f, 0.84f, 0.00f)},
- {"GOLDENROD", new Color(0.85f, 0.65f, 0.13f)},
- {"GRAY", new Color(0.75f, 0.75f, 0.75f)},
- {"GREEN", new Color(0.00f, 1.00f, 0.00f)},
- {"GREENYELLOW", new Color(0.68f, 1.00f, 0.18f)},
- {"HONEYDEW", new Color(0.94f, 1.00f, 0.94f)},
- {"HOTPINK", new Color(1.00f, 0.41f, 0.71f)},
- {"INDIANRED", new Color(0.80f, 0.36f, 0.36f)},
- {"INDIGO", new Color(0.29f, 0.00f, 0.51f)},
- {"IVORY", new Color(1.00f, 1.00f, 0.94f)},
- {"KHAKI", new Color(0.94f, 0.90f, 0.55f)},
- {"LAVENDER", new Color(0.90f, 0.90f, 0.98f)},
- {"LAVENDERBLUSH", new Color(1.00f, 0.94f, 0.96f)},
- {"LAWNGREEN", new Color(0.49f, 0.99f, 0.00f)},
- {"LEMONCHIFFON", new Color(1.00f, 0.98f, 0.80f)},
- {"LIGHTBLUE", new Color(0.68f, 0.85f, 0.90f)},
- {"LIGHTCORAL", new Color(0.94f, 0.50f, 0.50f)},
- {"LIGHTCYAN", new Color(0.88f, 1.00f, 1.00f)},
- {"LIGHTGOLDENROD", new Color(0.98f, 0.98f, 0.82f)},
- {"LIGHTGRAY", new Color(0.83f, 0.83f, 0.83f)},
- {"LIGHTGREEN", new Color(0.56f, 0.93f, 0.56f)},
- {"LIGHTPINK", new Color(1.00f, 0.71f, 0.76f)},
- {"LIGHTSALMON", new Color(1.00f, 0.63f, 0.48f)},
- {"LIGHTSEAGREEN", new Color(0.13f, 0.70f, 0.67f)},
- {"LIGHTSKYBLUE", new Color(0.53f, 0.81f, 0.98f)},
- {"LIGHTSLATEGRAY", new Color(0.47f, 0.53f, 0.60f)},
- {"LIGHTSTEELBLUE", new Color(0.69f, 0.77f, 0.87f)},
- {"LIGHTYELLOW", new Color(1.00f, 1.00f, 0.88f)},
- {"LIME", new Color(0.00f, 1.00f, 0.00f)},
- {"LIMEGREEN", new Color(0.20f, 0.80f, 0.20f)},
- {"LINEN", new Color(0.98f, 0.94f, 0.90f)},
- {"MAGENTA", new Color(1.00f, 0.00f, 1.00f)},
- {"MAROON", new Color(0.69f, 0.19f, 0.38f)},
- {"MEDIUMAQUAMARINE", new Color(0.40f, 0.80f, 0.67f)},
- {"MEDIUMBLUE", new Color(0.00f, 0.00f, 0.80f)},
- {"MEDIUMORCHID", new Color(0.73f, 0.33f, 0.83f)},
- {"MEDIUMPURPLE", new Color(0.58f, 0.44f, 0.86f)},
- {"MEDIUMSEAGREEN", new Color(0.24f, 0.70f, 0.44f)},
- {"MEDIUMSLATEBLUE", new Color(0.48f, 0.41f, 0.93f)},
- {"MEDIUMSPRINGGREEN", new Color(0.00f, 0.98f, 0.60f)},
- {"MEDIUMTURQUOISE", new Color(0.28f, 0.82f, 0.80f)},
- {"MEDIUMVIOLETRED", new Color(0.78f, 0.08f, 0.52f)},
- {"MIDNIGHTBLUE", new Color(0.10f, 0.10f, 0.44f)},
- {"MINTCREAM", new Color(0.96f, 1.00f, 0.98f)},
- {"MISTYROSE", new Color(1.00f, 0.89f, 0.88f)},
- {"MOCCASIN", new Color(1.00f, 0.89f, 0.71f)},
- {"NAVAJOWHITE", new Color(1.00f, 0.87f, 0.68f)},
- {"NAVYBLUE", new Color(0.00f, 0.00f, 0.50f)},
- {"OLDLACE", new Color(0.99f, 0.96f, 0.90f)},
- {"OLIVE", new Color(0.50f, 0.50f, 0.00f)},
- {"OLIVEDRAB", new Color(0.42f, 0.56f, 0.14f)},
- {"ORANGE", new Color(1.00f, 0.65f, 0.00f)},
- {"ORANGERED", new Color(1.00f, 0.27f, 0.00f)},
- {"ORCHID", new Color(0.85f, 0.44f, 0.84f)},
- {"PALEGOLDENROD", new Color(0.93f, 0.91f, 0.67f)},
- {"PALEGREEN", new Color(0.60f, 0.98f, 0.60f)},
- {"PALETURQUOISE", new Color(0.69f, 0.93f, 0.93f)},
- {"PALEVIOLETRED", new Color(0.86f, 0.44f, 0.58f)},
- {"PAPAYAWHIP", new Color(1.00f, 0.94f, 0.84f)},
- {"PEACHPUFF", new Color(1.00f, 0.85f, 0.73f)},
- {"PERU", new Color(0.80f, 0.52f, 0.25f)},
- {"PINK", new Color(1.00f, 0.75f, 0.80f)},
- {"PLUM", new Color(0.87f, 0.63f, 0.87f)},
- {"POWDERBLUE", new Color(0.69f, 0.88f, 0.90f)},
- {"PURPLE", new Color(0.63f, 0.13f, 0.94f)},
- {"REBECCAPURPLE", new Color(0.40f, 0.20f, 0.60f)},
- {"RED", new Color(1.00f, 0.00f, 0.00f)},
- {"ROSYBROWN", new Color(0.74f, 0.56f, 0.56f)},
- {"ROYALBLUE", new Color(0.25f, 0.41f, 0.88f)},
- {"SADDLEBROWN", new Color(0.55f, 0.27f, 0.07f)},
- {"SALMON", new Color(0.98f, 0.50f, 0.45f)},
- {"SANDYBROWN", new Color(0.96f, 0.64f, 0.38f)},
- {"SEAGREEN", new Color(0.18f, 0.55f, 0.34f)},
- {"SEASHELL", new Color(1.00f, 0.96f, 0.93f)},
- {"SIENNA", new Color(0.63f, 0.32f, 0.18f)},
- {"SILVER", new Color(0.75f, 0.75f, 0.75f)},
- {"SKYBLUE", new Color(0.53f, 0.81f, 0.92f)},
- {"SLATEBLUE", new Color(0.42f, 0.35f, 0.80f)},
- {"SLATEGRAY", new Color(0.44f, 0.50f, 0.56f)},
- {"SNOW", new Color(1.00f, 0.98f, 0.98f)},
- {"SPRINGGREEN", new Color(0.00f, 1.00f, 0.50f)},
- {"STEELBLUE", new Color(0.27f, 0.51f, 0.71f)},
- {"TAN", new Color(0.82f, 0.71f, 0.55f)},
- {"TEAL", new Color(0.00f, 0.50f, 0.50f)},
- {"THISTLE", new Color(0.85f, 0.75f, 0.85f)},
- {"TOMATO", new Color(1.00f, 0.39f, 0.28f)},
- {"TRANSPARENT", new Color(1.00f, 1.00f, 1.00f, 0.00f)},
- {"TURQUOISE", new Color(0.25f, 0.88f, 0.82f)},
- {"VIOLET", new Color(0.93f, 0.51f, 0.93f)},
- {"WEBGRAY", new Color(0.50f, 0.50f, 0.50f)},
- {"WEBGREEN", new Color(0.00f, 0.50f, 0.00f)},
- {"WEBMAROON", new Color(0.50f, 0.00f, 0.00f)},
- {"WEBPURPLE", new Color(0.50f, 0.00f, 0.50f)},
- {"WHEAT", new Color(0.96f, 0.87f, 0.70f)},
- {"WHITE", new Color(1.00f, 1.00f, 1.00f)},
- {"WHITESMOKE", new Color(0.96f, 0.96f, 0.96f)},
- {"YELLOW", new Color(1.00f, 1.00f, 0.00f)},
- {"YELLOWGREEN", new Color(0.60f, 0.80f, 0.20f)},
+ { "ALICE_BLUE", new Color(0xF0F8FFFF) },
+ { "ANTIQUE_WHITE", new Color(0xFAEBD7FF) },
+ { "AQUA", new Color(0x00FFFFFF) },
+ { "AQUAMARINE", new Color(0x7FFFD4FF) },
+ { "AZURE", new Color(0xF0FFFFFF) },
+ { "BEIGE", new Color(0xF5F5DCFF) },
+ { "BISQUE", new Color(0xFFE4C4FF) },
+ { "BLACK", new Color(0x000000FF) },
+ { "BLANCHED_ALMOND", new Color(0xFFEBCDFF) },
+ { "BLUE", new Color(0x0000FFFF) },
+ { "BLUE_VIOLET", new Color(0x8A2BE2FF) },
+ { "BROWN", new Color(0xA52A2AFF) },
+ { "BURLYWOOD", new Color(0xDEB887FF) },
+ { "CADET_BLUE", new Color(0x5F9EA0FF) },
+ { "CHARTREUSE", new Color(0x7FFF00FF) },
+ { "CHOCOLATE", new Color(0xD2691EFF) },
+ { "CORAL", new Color(0xFF7F50FF) },
+ { "CORNFLOWER_BLUE", new Color(0x6495EDFF) },
+ { "CORNSILK", new Color(0xFFF8DCFF) },
+ { "CRIMSON", new Color(0xDC143CFF) },
+ { "CYAN", new Color(0x00FFFFFF) },
+ { "DARK_BLUE", new Color(0x00008BFF) },
+ { "DARK_CYAN", new Color(0x008B8BFF) },
+ { "DARK_GOLDENROD", new Color(0xB8860BFF) },
+ { "DARK_GRAY", new Color(0xA9A9A9FF) },
+ { "DARK_GREEN", new Color(0x006400FF) },
+ { "DARK_KHAKI", new Color(0xBDB76BFF) },
+ { "DARK_MAGENTA", new Color(0x8B008BFF) },
+ { "DARK_OLIVE_GREEN", new Color(0x556B2FFF) },
+ { "DARK_ORANGE", new Color(0xFF8C00FF) },
+ { "DARK_ORCHID", new Color(0x9932CCFF) },
+ { "DARK_RED", new Color(0x8B0000FF) },
+ { "DARK_SALMON", new Color(0xE9967AFF) },
+ { "DARK_SEA_GREEN", new Color(0x8FBC8FFF) },
+ { "DARK_SLATE_BLUE", new Color(0x483D8BFF) },
+ { "DARK_SLATE_GRAY", new Color(0x2F4F4FFF) },
+ { "DARK_TURQUOISE", new Color(0x00CED1FF) },
+ { "DARK_VIOLET", new Color(0x9400D3FF) },
+ { "DEEP_PINK", new Color(0xFF1493FF) },
+ { "DEEP_SKY_BLUE", new Color(0x00BFFFFF) },
+ { "DIM_GRAY", new Color(0x696969FF) },
+ { "DODGER_BLUE", new Color(0x1E90FFFF) },
+ { "FIREBRICK", new Color(0xB22222FF) },
+ { "FLORAL_WHITE", new Color(0xFFFAF0FF) },
+ { "FOREST_GREEN", new Color(0x228B22FF) },
+ { "FUCHSIA", new Color(0xFF00FFFF) },
+ { "GAINSBORO", new Color(0xDCDCDCFF) },
+ { "GHOST_WHITE", new Color(0xF8F8FFFF) },
+ { "GOLD", new Color(0xFFD700FF) },
+ { "GOLDENROD", new Color(0xDAA520FF) },
+ { "GRAY", new Color(0xBEBEBEFF) },
+ { "GREEN", new Color(0x00FF00FF) },
+ { "GREEN_YELLOW", new Color(0xADFF2FFF) },
+ { "HONEYDEW", new Color(0xF0FFF0FF) },
+ { "HOT_PINK", new Color(0xFF69B4FF) },
+ { "INDIAN_RED", new Color(0xCD5C5CFF) },
+ { "INDIGO", new Color(0x4B0082FF) },
+ { "IVORY", new Color(0xFFFFF0FF) },
+ { "KHAKI", new Color(0xF0E68CFF) },
+ { "LAVENDER", new Color(0xE6E6FAFF) },
+ { "LAVENDER_BLUSH", new Color(0xFFF0F5FF) },
+ { "LAWN_GREEN", new Color(0x7CFC00FF) },
+ { "LEMON_CHIFFON", new Color(0xFFFACDFF) },
+ { "LIGHT_BLUE", new Color(0xADD8E6FF) },
+ { "LIGHT_CORAL", new Color(0xF08080FF) },
+ { "LIGHT_CYAN", new Color(0xE0FFFFFF) },
+ { "LIGHT_GOLDENROD", new Color(0xFAFAD2FF) },
+ { "LIGHT_GRAY", new Color(0xD3D3D3FF) },
+ { "LIGHT_GREEN", new Color(0x90EE90FF) },
+ { "LIGHT_PINK", new Color(0xFFB6C1FF) },
+ { "LIGHT_SALMON", new Color(0xFFA07AFF) },
+ { "LIGHT_SEA_GREEN", new Color(0x20B2AAFF) },
+ { "LIGHT_SKY_BLUE", new Color(0x87CEFAFF) },
+ { "LIGHT_SLATE_GRAY", new Color(0x778899FF) },
+ { "LIGHT_STEEL_BLUE", new Color(0xB0C4DEFF) },
+ { "LIGHT_YELLOW", new Color(0xFFFFE0FF) },
+ { "LIME", new Color(0x00FF00FF) },
+ { "LIME_GREEN", new Color(0x32CD32FF) },
+ { "LINEN", new Color(0xFAF0E6FF) },
+ { "MAGENTA", new Color(0xFF00FFFF) },
+ { "MAROON", new Color(0xB03060FF) },
+ { "MEDIUM_AQUAMARINE", new Color(0x66CDAAFF) },
+ { "MEDIUM_BLUE", new Color(0x0000CDFF) },
+ { "MEDIUM_ORCHID", new Color(0xBA55D3FF) },
+ { "MEDIUM_PURPLE", new Color(0x9370DBFF) },
+ { "MEDIUM_SEA_GREEN", new Color(0x3CB371FF) },
+ { "MEDIUM_SLATE_BLUE", new Color(0x7B68EEFF) },
+ { "MEDIUM_SPRING_GREEN", new Color(0x00FA9AFF) },
+ { "MEDIUM_TURQUOISE", new Color(0x48D1CCFF) },
+ { "MEDIUM_VIOLET_RED", new Color(0xC71585FF) },
+ { "MIDNIGHT_BLUE", new Color(0x191970FF) },
+ { "MINT_CREAM", new Color(0xF5FFFAFF) },
+ { "MISTY_ROSE", new Color(0xFFE4E1FF) },
+ { "MOCCASIN", new Color(0xFFE4B5FF) },
+ { "NAVAJO_WHITE", new Color(0xFFDEADFF) },
+ { "NAVY_BLUE", new Color(0x000080FF) },
+ { "OLD_LACE", new Color(0xFDF5E6FF) },
+ { "OLIVE", new Color(0x808000FF) },
+ { "OLIVE_DRAB", new Color(0x6B8E23FF) },
+ { "ORANGE", new Color(0xFFA500FF) },
+ { "ORANGE_RED", new Color(0xFF4500FF) },
+ { "ORCHID", new Color(0xDA70D6FF) },
+ { "PALE_GOLDENROD", new Color(0xEEE8AAFF) },
+ { "PALE_GREEN", new Color(0x98FB98FF) },
+ { "PALE_TURQUOISE", new Color(0xAFEEEEFF) },
+ { "PALE_VIOLET_RED", new Color(0xDB7093FF) },
+ { "PAPAYA_WHIP", new Color(0xFFEFD5FF) },
+ { "PEACH_PUFF", new Color(0xFFDAB9FF) },
+ { "PERU", new Color(0xCD853FFF) },
+ { "PINK", new Color(0xFFC0CBFF) },
+ { "PLUM", new Color(0xDDA0DDFF) },
+ { "POWDER_BLUE", new Color(0xB0E0E6FF) },
+ { "PURPLE", new Color(0xA020F0FF) },
+ { "REBECCA_PURPLE", new Color(0x663399FF) },
+ { "RED", new Color(0xFF0000FF) },
+ { "ROSY_BROWN", new Color(0xBC8F8FFF) },
+ { "ROYAL_BLUE", new Color(0x4169E1FF) },
+ { "SADDLE_BROWN", new Color(0x8B4513FF) },
+ { "SALMON", new Color(0xFA8072FF) },
+ { "SANDY_BROWN", new Color(0xF4A460FF) },
+ { "SEA_GREEN", new Color(0x2E8B57FF) },
+ { "SEASHELL", new Color(0xFFF5EEFF) },
+ { "SIENNA", new Color(0xA0522DFF) },
+ { "SILVER", new Color(0xC0C0C0FF) },
+ { "SKY_BLUE", new Color(0x87CEEBFF) },
+ { "SLATE_BLUE", new Color(0x6A5ACDFF) },
+ { "SLATE_GRAY", new Color(0x708090FF) },
+ { "SNOW", new Color(0xFFFAFAFF) },
+ { "SPRING_GREEN", new Color(0x00FF7FFF) },
+ { "STEEL_BLUE", new Color(0x4682B4FF) },
+ { "TAN", new Color(0xD2B48CFF) },
+ { "TEAL", new Color(0x008080FF) },
+ { "THISTLE", new Color(0xD8BFD8FF) },
+ { "TOMATO", new Color(0xFF6347FF) },
+ { "TRANSPARENT", new Color(0xFFFFFF00) },
+ { "TURQUOISE", new Color(0x40E0D0FF) },
+ { "VIOLET", new Color(0xEE82EEFF) },
+ { "WEB_GRAY", new Color(0x808080FF) },
+ { "WEB_GREEN", new Color(0x008000FF) },
+ { "WEB_MAROON", new Color(0x800000FF) },
+ { "WEB_PURPLE", new Color(0x800080FF) },
+ { "WHEAT", new Color(0xF5DEB3FF) },
+ { "WHITE", new Color(0xFFFFFFFF) },
+ { "WHITE_SMOKE", new Color(0xF5F5F5FF) },
+ { "YELLOW", new Color(0xFFFF00FF) },
+ { "YELLOW_GREEN", new Color(0x9ACD32FF) },
};
#pragma warning disable CS1591 // Disable warning: "Missing XML comment for publicly visible type or member"
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs
new file mode 100644
index 0000000000..42f19ace1a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs
@@ -0,0 +1,98 @@
+#nullable enable
+
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using Godot.Bridge;
+
+namespace Godot;
+
+/// <summary>
+/// Provides a GCHandle that becomes weak when unloading the assembly load context, without having
+/// to manually replace the GCHandle. This hides all the complexity of releasing strong GC handles
+/// to allow the assembly load context to unload properly.
+///
+/// Internally, a strong CustomGCHandle actually contains a weak GCHandle, while the actual strong
+/// reference is stored in a static table.
+/// </summary>
+public static class CustomGCHandle
+{
+ // ConditionalWeakTable uses DependentHandle, so it stores weak references.
+ // Having the assembly load context as key won't prevent it from unloading.
+ private static ConditionalWeakTable<AssemblyLoadContext, object?> _alcsBeingUnloaded = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static bool IsAlcBeingUnloaded(AssemblyLoadContext alc) => _alcsBeingUnloaded.TryGetValue(alc, out _);
+
+ // ReSharper disable once RedundantNameQualifier
+ private static ConcurrentDictionary<
+ AssemblyLoadContext,
+ ConcurrentDictionary<GCHandle, object>
+ > _strongReferencesByAlc = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OnAlcUnloading(AssemblyLoadContext alc)
+ {
+ _alcsBeingUnloaded.Add(alc, null);
+
+ if (_strongReferencesByAlc.TryRemove(alc, out var strongReferences))
+ {
+ strongReferences.Clear();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static GCHandle AllocStrong(object value)
+ => AllocStrong(value, value.GetType());
+
+ public static GCHandle AllocStrong(object value, Type valueType)
+ {
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(valueType.Assembly);
+
+ if (alc != null)
+ {
+ var weakHandle = GCHandle.Alloc(value, GCHandleType.Weak);
+
+ if (!IsAlcBeingUnloaded(alc))
+ {
+ var strongReferences = _strongReferencesByAlc.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ strongReferences.TryAdd(weakHandle, value);
+ }
+
+ return weakHandle;
+ }
+ }
+
+ return GCHandle.Alloc(value, GCHandleType.Normal);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static GCHandle AllocWeak(object value) => GCHandle.Alloc(value, GCHandleType.Weak);
+
+ public static void Free(GCHandle handle)
+ {
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ var target = handle.Target;
+
+ if (target != null)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(target.GetType().Assembly);
+
+ if (alc != null && _strongReferencesByAlc.TryGetValue(alc, out var strongReferences))
+ _ = strongReferences.TryRemove(handle, out _);
+ }
+ }
+
+ handle.Free();
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
index edfe3464ec..6fbc04702a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
@@ -1,13 +1,18 @@
using System;
using System.Diagnostics;
using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
+using Godot.NativeInterop;
+
+#nullable enable
namespace Godot
{
internal static class DebuggingUtils
{
- internal static void AppendTypeName(this StringBuilder sb, Type type)
+ private static void AppendTypeName(this StringBuilder sb, Type type)
{
if (type.IsPrimitive)
sb.Append(type.Name);
@@ -16,21 +21,97 @@ namespace Godot
else
sb.Append(type);
- sb.Append(" ");
+ sb.Append(' ');
}
- public static void InstallTraceListener()
+ internal static void InstallTraceListener()
{
Trace.Listeners.Clear();
Trace.Listeners.Add(new GodotTraceListener());
}
- public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl)
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal ref struct godot_stack_info
+ {
+ public godot_string File;
+ public godot_string Func;
+ public int Line;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal ref struct godot_stack_info_vector
{
- fileName = frame.GetFileName();
- fileLineNumber = frame.GetFileLineNumber();
+ private IntPtr _writeProxy;
+ private unsafe godot_stack_info* _ptr;
+
+ public readonly unsafe godot_stack_info* Elements
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public void Resize(int size)
+ {
+ if (size < 0)
+ throw new ArgumentOutOfRangeException(nameof(size));
+ var err = NativeFuncs.godotsharp_stack_info_vector_resize(ref this, size);
+ if (err != Error.Ok)
+ throw new InvalidOperationException("Failed to resize vector. Error code is: " + err.ToString());
+ }
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_stack_info_vector_destroy(ref this);
+ _ptr = null;
+ }
+ }
- MethodBase methodBase = frame.GetMethod();
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetCurrentStackInfo(void* destVector)
+ {
+ try
+ {
+ var vector = (godot_stack_info_vector*)destVector;
+ var stackTrace = new StackTrace(skipFrames: 1, fNeedFileInfo: true);
+ int frameCount = stackTrace.FrameCount;
+
+ if (frameCount == 0)
+ return;
+
+ vector->Resize(frameCount);
+
+ int i = 0;
+ foreach (StackFrame frame in stackTrace.GetFrames())
+ {
+ string? fileName = frame.GetFileName();
+ int fileLineNumber = frame.GetFileLineNumber();
+
+ GetStackFrameMethodDecl(frame, out string methodDecl);
+
+ godot_stack_info* stackInfo = &vector->Elements[i];
+
+ // Assign directly to element in Vector. This way we don't need to worry
+ // about disposal if an exception is thrown. The Vector takes care of it.
+ stackInfo->File = Marshaling.ConvertStringToNative(fileName);
+ stackInfo->Func = Marshaling.ConvertStringToNative(methodDecl);
+ stackInfo->Line = fileLineNumber;
+
+ i++;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ internal static void GetStackFrameMethodDecl(StackFrame frame, out string methodDecl)
+ {
+ MethodBase? methodBase = frame.GetMethod();
if (methodBase == null)
{
@@ -43,15 +124,15 @@ namespace Godot
if (methodBase is MethodInfo)
sb.AppendTypeName(((MethodInfo)methodBase).ReturnType);
- sb.Append(methodBase.DeclaringType.FullName);
- sb.Append(".");
+ sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>");
+ sb.Append('.');
sb.Append(methodBase.Name);
if (methodBase.IsGenericMethod)
{
Type[] genericParams = methodBase.GetGenericArguments();
- sb.Append("<");
+ sb.Append('<');
for (int j = 0; j < genericParams.Length; j++)
{
@@ -61,10 +142,10 @@ namespace Godot
sb.AppendTypeName(genericParams[j]);
}
- sb.Append(">");
+ sb.Append('>');
}
- sb.Append("(");
+ sb.Append('(');
bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0;
@@ -81,7 +162,7 @@ namespace Godot
sb.AppendTypeName(parameter[i].ParameterType);
}
- sb.Append(")");
+ sb.Append(')');
methodDecl = sb.ToString();
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index 1dca9e6ea7..266038a0af 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -1,14 +1,72 @@
+#nullable enable
+
using System;
using System.Collections.Generic;
-using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
internal static class DelegateUtils
{
+ [UnmanagedCallersOnly]
+ internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
+ {
+ try
+ {
+ var @delegateA = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleA).Target;
+ var @delegateB = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleB).Target;
+ return (@delegateA! == @delegateB!).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc,
+ godot_variant* outRet)
+ {
+ try
+ {
+ // 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.ConvertVariantToManagedObjectOfType(
+ *args[i], parameterInfos[i].ParameterType);
+ }
+
+ object? invokeRet = @delegate.DynamicInvoke(managedArgs);
+
+ *outRet = Marshaling.ConvertManagedObjectToVariant(invokeRet);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRet = default;
+ }
+ }
+
+ // TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).
+
private enum TargetKind : uint
{
Static,
@@ -39,20 +97,20 @@ namespace Godot
}
}
- if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
+ if (TrySerializeSingleDelegate(@delegate, out byte[]? buffer))
{
- serializedData.Add(buffer);
+ serializedData.Add((Span<byte>)buffer);
return true;
}
return false;
}
- private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
+ private static bool TrySerializeSingleDelegate(Delegate @delegate, [MaybeNullWhen(false)] out byte[] buffer)
{
buffer = null;
- object target = @delegate.Target;
+ object? target = @delegate.Target;
switch (target)
{
@@ -72,12 +130,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 +153,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.
@@ -121,8 +181,18 @@ namespace Godot
if (variantType == Variant.Type.Nil)
return false;
+ static byte[] Var2Bytes(in godot_variant var)
+ {
+ NativeFuncs.godotsharp_var2bytes(var, false.ToGodotBool(), out var varBytes);
+ using (varBytes)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
+ }
+
writer.Write(field.Name);
- byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
+
+ var fieldValue = field.GetValue(target);
+ using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue);
+ byte[] valueBuffer = Var2Bytes(fieldValueVariant);
writer.Write(valueBuffer.Length);
writer.Write(valueBuffer);
}
@@ -139,9 +209,6 @@ namespace Godot
private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
{
- if (methodInfo == null)
- return false;
-
SerializeType(writer, methodInfo.DeclaringType);
writer.Write(methodInfo.Name);
@@ -180,7 +247,7 @@ namespace Godot
return true;
}
- private static void SerializeType(BinaryWriter writer, Type type)
+ private static void SerializeType(BinaryWriter writer, Type? type)
{
if (type == null)
{
@@ -195,9 +262,8 @@ namespace Godot
int genericArgumentsCount = genericArgs.Length;
writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
+ writer.Write(genericTypeDef.Assembly.GetName().Name ?? "");
+ writer.Write(genericTypeDef.FullName ?? genericTypeDef.ToString());
for (int i = 0; i < genericArgs.Length; i++)
SerializeType(writer, genericArgs[i]);
@@ -207,17 +273,71 @@ namespace Godot
int genericArgumentsCount = 0;
writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = type.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
+ writer.Write(type.Assembly.GetName().Name ?? "");
+ writer.Write(type.FullName ?? type.ToString());
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle,
+ godot_array* nSerializedData)
+ {
+ try
+ {
+ var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
+
+ var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
+
+ return TrySerializeDelegate(@delegate, serializedData)
+ .ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
}
}
- private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool TryDeserializeDelegateWithGCHandle(godot_array* nSerializedData,
+ IntPtr* delegateGCHandle)
{
+ try
+ {
+ var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
+
+ if (TryDeserializeDelegate(serializedData, out Delegate? @delegate))
+ {
+ *delegateGCHandle = GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(@delegate));
+ return godot_bool.True;
+ }
+ else
+ {
+ *delegateGCHandle = IntPtr.Zero;
+ return godot_bool.False;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *delegateGCHandle = default;
+ return godot_bool.False;
+ }
+ }
+
+ internal static bool TryDeserializeDelegate(Collections.Array serializedData,
+ [MaybeNullWhen(false)] out Delegate @delegate)
+ {
+ @delegate = null;
+
if (serializedData.Count == 1)
{
- object elem = serializedData[0];
+ var elem = serializedData[0].Obj;
+
+ if (elem == null)
+ return false;
if (elem is Collections.Array multiCastData)
return TryDeserializeDelegate(multiCastData, out @delegate);
@@ -225,20 +345,23 @@ namespace Godot
return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
}
- @delegate = null;
-
var delegates = new List<Delegate>(serializedData.Count);
- foreach (object elem in serializedData)
+ foreach (Variant variantElem in serializedData)
{
+ var elem = variantElem.Obj;
+
+ if (elem == null)
+ continue;
+
if (elem is Collections.Array multiCastData)
{
- if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
+ if (TryDeserializeDelegate(multiCastData, out Delegate? oneDelegate))
delegates.Add(oneDelegate);
}
else
{
- if (TryDeserializeSingleDelegate((byte[])elem, out Delegate oneDelegate))
+ if (TryDeserializeSingleDelegate((byte[])elem, out Delegate? oneDelegate))
delegates.Add(oneDelegate);
}
}
@@ -246,11 +369,11 @@ namespace Godot
if (delegates.Count <= 0)
return false;
- @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
+ @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray())!;
return true;
}
- private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
+ private static bool TryDeserializeSingleDelegate(byte[] buffer, [MaybeNullWhen(false)] out Delegate @delegate)
{
@delegate = null;
@@ -263,49 +386,59 @@ namespace Godot
{
case TargetKind.Static:
{
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo, throwOnBindFailure: false);
+
+ if (@delegate == null)
return false;
- @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
return true;
}
case TargetKind.GodotObject:
{
ulong objectId = reader.ReadUInt64();
+ // ReSharper disable once RedundantNameQualifier
Godot.Object godotObject = GD.InstanceFromId(objectId);
if (godotObject == null)
return false;
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo,
+ throwOnBindFailure: false);
+
+ if (@delegate == null)
return false;
- @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
return true;
}
case TargetKind.CompilerGenerated:
{
- Type targetType = DeserializeType(reader);
+ Type? targetType = DeserializeType(reader);
if (targetType == null)
return false;
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
return false;
int fieldCount = reader.ReadInt32();
- object recreatedTarget = Activator.CreateInstance(targetType);
+ object recreatedTarget = Activator.CreateInstance(targetType)!;
for (int i = 0; i < fieldCount; i++)
{
@@ -313,11 +446,17 @@ namespace Godot
int valueBufferLength = reader.ReadInt32();
byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
- FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
+ FieldInfo? fieldInfo = targetType.GetField(name,
+ BindingFlags.Instance | BindingFlags.Public);
fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
}
- @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
+ @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo,
+ throwOnBindFailure: false);
+
+ if (@delegate == null)
+ return false;
+
return true;
}
default:
@@ -326,18 +465,22 @@ namespace Godot
}
}
- private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
+ private static bool TryDeserializeMethodInfo(BinaryReader reader,
+ [MaybeNullWhen(false)] out MethodInfo methodInfo)
{
methodInfo = null;
- Type declaringType = DeserializeType(reader);
+ Type? declaringType = DeserializeType(reader);
+
+ if (declaringType == null)
+ return false;
string methodName = reader.ReadString();
int flags = reader.ReadInt32();
bool hasReturn = reader.ReadBoolean();
- Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
+ Type? returnType = hasReturn ? DeserializeType(reader) : typeof(void);
int parametersCount = reader.ReadInt32();
@@ -347,7 +490,7 @@ namespace Godot
for (int i = 0; i < parametersCount; i++)
{
- Type parameterType = DeserializeType(reader);
+ Type? parameterType = DeserializeType(reader);
if (parameterType == null)
return false;
parameterTypes[i] = parameterType;
@@ -361,15 +504,23 @@ namespace Godot
return methodInfo != null && methodInfo.ReturnType == returnType;
}
- private static Type DeserializeType(BinaryReader reader)
+ private static Type? DeserializeType(BinaryReader reader)
{
int genericArgumentsCount = reader.ReadInt32();
if (genericArgumentsCount == -1)
return null;
- string assemblyQualifiedName = reader.ReadString();
- var type = Type.GetType(assemblyQualifiedName);
+ string assemblyName = reader.ReadString();
+
+ if (assemblyName.Length == 0)
+ {
+ GD.PushError($"Missing assembly name of type when attempting to deserialize delegate");
+ return null;
+ }
+
+ string typeFullName = reader.ReadString();
+ var type = ReflectionUtils.FindTypeInLoadedAssemblies(assemblyName, typeFullName);
if (type == null)
return null; // Type not found
@@ -380,7 +531,7 @@ namespace Godot
for (int i = 0; i < genericArgumentsCount; i++)
{
- Type genericArgumentType = DeserializeType(reader);
+ Type? genericArgumentType = DeserializeType(reader);
if (genericArgumentType == null)
return null;
genericArgumentTypes[i] = genericArgumentType;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index e80b6af68f..95ad097192 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -1,79 +1,50 @@
using System;
using System.Collections.Generic;
using System.Collections;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot.Collections
{
- internal class DictionarySafeHandle : SafeHandle
- {
- public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
- {
- this.handle = handle;
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- protected override bool ReleaseHandle()
- {
- Dictionary.godot_icall_Dictionary_Dtor(handle);
- return true;
- }
- }
-
/// <summary>
/// Wrapper around Godot's Dictionary class, a dictionary of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine.
/// </summary>
- public class Dictionary : IDictionary, IDisposable
+ public sealed class Dictionary :
+ IDictionary<Variant, Variant>,
+ IReadOnlyDictionary<Variant, Variant>,
+ IDisposable
{
- private DictionarySafeHandle _safeHandle;
- private bool _disposed = false;
+ internal godot_dictionary.movable NativeValue;
+
+ private WeakReference<IDisposable> _weakReferenceToSelf;
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
/// </summary>
public Dictionary()
{
- _safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
+ NativeValue = (godot_dictionary.movable)NativeFuncs.godotsharp_dictionary_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
- /// <summary>
- /// Constructs a new <see cref="Dictionary"/> from the given dictionary's elements.
- /// </summary>
- /// <param name="dictionary">The dictionary to construct from.</param>
- /// <returns>A new Godot Dictionary.</returns>
- public Dictionary(IDictionary dictionary) : this()
- {
- if (dictionary == null)
- throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
-
- foreach (DictionaryEntry entry in dictionary)
- Add(entry.Key, entry.Value);
- }
-
- internal Dictionary(DictionarySafeHandle handle)
+ private Dictionary(godot_dictionary nativeValueToOwn)
{
- _safeHandle = handle;
+ NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ?
+ nativeValueToOwn :
+ NativeFuncs.godotsharp_dictionary_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
- internal Dictionary(IntPtr handle)
- {
- _safeHandle = new DictionarySafeHandle(handle);
- }
+ // Explicit name to make it very clear
+ internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn)
+ => new Dictionary(nativeValueToOwn);
- internal IntPtr GetPtr()
+ ~Dictionary()
{
- if (_disposed)
- throw new ObjectDisposedException(GetType().FullName);
-
- return _safeHandle.DangerousGetHandle();
+ Dispose(false);
}
/// <summary>
@@ -81,16 +52,19 @@ namespace Godot.Collections
/// </summary>
public void Dispose()
{
- if (_disposed)
- return;
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
- if (_safeHandle != null)
+ public void Dispose(bool disposing)
+ {
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
{
- _safeHandle.Dispose();
- _safeHandle = null;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
-
- _disposed = true;
}
/// <summary>
@@ -100,7 +74,10 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary Duplicate(bool deep = false)
{
- return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
+ godot_dictionary newDictionary;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_duplicate(ref self, deep.ToGodotBool(), out newDictionary);
+ return CreateTakingOwnershipOfDisposableValue(newDictionary);
}
// IDictionary
@@ -108,176 +85,250 @@ namespace Godot.Collections
/// <summary>
/// Gets the collection of keys in this <see cref="Dictionary"/>.
/// </summary>
- public ICollection Keys
+ public ICollection<Variant> Keys
{
get
{
- IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
- return new Array(new ArraySafeHandle(handle));
+ godot_array keysArray;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
+ return Array.CreateTakingOwnershipOfDisposableValue(keysArray);
}
}
/// <summary>
/// Gets the collection of elements in this <see cref="Dictionary"/>.
/// </summary>
- public ICollection Values
+ public ICollection<Variant> Values
{
get
{
- IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
- return new Array(new ArraySafeHandle(handle));
+ godot_array valuesArray;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
+ return Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
+ IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Keys => Keys;
+
+ IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Values => Values;
+
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));
- return (keys, values, count);
- }
+ var self = (godot_dictionary)NativeValue;
- bool IDictionary.IsFixedSize => false;
+ godot_array keysArray;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
+ var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray);
- bool IDictionary.IsReadOnly => false;
+ godot_array valuesArray;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out valuesArray);
+ var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
+
+ int count = NativeFuncs.godotsharp_dictionary_count(ref self);
+
+ return (keys, values, count);
+ }
/// <summary>
- /// Returns the object at the given <paramref name="key"/>.
+ /// Returns the value at the given <paramref name="key"/>.
/// </summary>
- /// <value>The object at the given <paramref name="key"/>.</value>
- public object this[object key]
+ /// <value>The value at the given <paramref name="key"/>.</value>
+ public Variant this[Variant key]
{
- get => godot_icall_Dictionary_GetValue(GetPtr(), key);
- set => godot_icall_Dictionary_SetValue(GetPtr(), key, value);
+ get
+ {
+ var self = (godot_dictionary)NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ (godot_variant)key.NativeVar, out godot_variant value).ToBool())
+ {
+ return Variant.CreateTakingOwnershipOfDisposableValue(value);
+ }
+ else
+ {
+ throw new KeyNotFoundException();
+ }
+ }
+ set
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_set_value(ref self,
+ (godot_variant)key.NativeVar, (godot_variant)value.NativeVar);
+ }
}
/// <summary>
- /// Adds an object <paramref name="value"/> at key <paramref name="key"/>
+ /// Adds an value <paramref name="value"/> at key <paramref name="key"/>
/// to this <see cref="Dictionary"/>.
/// </summary>
- /// <param name="key">The key at which to add the object.</param>
- /// <param name="value">The object to add.</param>
- public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value);
+ /// <param name="key">The key at which to add the value.</param>
+ /// <param name="value">The value to add.</param>
+ public void Add(Variant key, Variant value)
+ {
+ var variantKey = (godot_variant)key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
+ throw new ArgumentException("An element with the same key already exists", nameof(key));
+
+ godot_variant variantValue = (godot_variant)value.NativeVar;
+ NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
+ }
+
+ void ICollection<KeyValuePair<Variant, Variant>>.Add(KeyValuePair<Variant, Variant> item)
+ => Add(item.Key, item.Value);
/// <summary>
/// Erases all items from this <see cref="Dictionary"/>.
/// </summary>
- public void Clear() => godot_icall_Dictionary_Clear(GetPtr());
+ public void Clear()
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_clear(ref self);
+ }
/// <summary>
/// Checks if this <see cref="Dictionary"/> contains the given key.
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
- public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ public bool ContainsKey(Variant key)
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool();
+ }
- /// <summary>
- /// Gets an enumerator for this <see cref="Dictionary"/>.
- /// </summary>
- /// <returns>An enumerator.</returns>
- public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this);
+ public bool Contains(KeyValuePair<Variant, Variant> item)
+ {
+ godot_variant variantKey = (godot_variant)item.Key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ godot_variant variantValue = (godot_variant)item.Value.NativeVar;
+ return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
+ }
+ }
/// <summary>
/// Removes an element from this <see cref="Dictionary"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
- public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ public bool Remove(Variant key)
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool();
+ }
- // ICollection
+ public bool Remove(KeyValuePair<Variant, Variant> item)
+ {
+ godot_variant variantKey = (godot_variant)item.Key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
- object ICollection.SyncRoot => this;
+ godot_variant variantValue = (godot_variant)item.Value.NativeVar;
+ if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
+ {
+ return NativeFuncs.godotsharp_dictionary_remove_key(
+ ref self, variantKey).ToBool();
+ }
- bool ICollection.IsSynchronized => false;
+ return false;
+ }
+ }
/// <summary>
/// Returns the number of elements in this <see cref="Dictionary"/>.
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count => godot_icall_Dictionary_Count(GetPtr());
+ public int Count
+ {
+ get
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_count(ref self);
+ }
+ }
+
+ bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false;
+
+ public bool TryGetValue(Variant key, out Variant value)
+ {
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ (godot_variant)key.NativeVar, out godot_variant retValue).ToBool();
+
+ value = found ? Variant.CreateTakingOwnershipOfDisposableValue(retValue) : default;
+
+ return found;
+ }
/// <summary>
- /// Copies the elements of this <see cref="Dictionary"/> to the given
- /// untyped C# array, starting at the given index.
+ /// Copies the elements of this <see cref="Dictionary"/> to the given untyped
+ /// <see cref="KeyValuePair{TKey, TValue}"/> array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
- /// <param name="index">The index to start at.</param>
- public void CopyTo(System.Array array, int index)
+ /// <param name="arrayIndex">The index to start at.</param>
+ public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex)
{
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 (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "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.");
+ 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.SetValue(new DictionaryEntry(keys[i], values[i]), index);
- index++;
+ array[arrayIndex] = new(keys[i], values[i]);
+ arrayIndex++;
}
}
// IEnumerable
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- private class DictionaryEnumerator : IDictionaryEnumerator
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
+ public IEnumerator<KeyValuePair<Variant, Variant>> GetEnumerator()
{
- private readonly Dictionary _dictionary;
- private readonly int _count;
- private int _index = -1;
- private bool _dirty = true;
-
- private DictionaryEntry _entry;
-
- public DictionaryEnumerator(Dictionary dictionary)
- {
- _dictionary = dictionary;
- _count = dictionary.Count;
- }
-
- public object Current => Entry;
-
- public DictionaryEntry Entry
- {
- get
- {
- if (_dirty)
- {
- UpdateEntry();
- }
- return _entry;
- }
- }
-
- private void UpdateEntry()
+ for (int i = 0; i < Count; i++)
{
- _dirty = false;
- godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value);
- _entry = new DictionaryEntry(key, value);
+ yield return GetKeyValuePair(i);
}
+ }
- public object Key => Entry.Key;
-
- public object Value => Entry.Value;
-
- public bool MoveNext()
- {
- _index++;
- _dirty = true;
- return _index < _count;
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- public void Reset()
- {
- _index = -1;
- _dirty = true;
- }
+ private KeyValuePair<Variant, Variant> GetKeyValuePair(int index)
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
+ out godot_variant key,
+ out godot_variant value);
+ return new KeyValuePair<Variant, Variant>(Variant.CreateTakingOwnershipOfDisposableValue(key),
+ Variant.CreateTakingOwnershipOfDisposableValue(value));
}
/// <summary>
@@ -286,74 +337,11 @@ namespace Godot.Collections
/// <returns>A string representation of this dictionary.</returns>
public override string ToString()
{
- return godot_icall_Dictionary_ToString(GetPtr());
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_to_string(ref self, out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Ctor();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Dictionary_Count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
-
- [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);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
-
- [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);
}
/// <summary>
@@ -364,16 +352,51 @@ namespace Godot.Collections
/// </summary>
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
- public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>
+ public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> :
+ IDictionary<TKey, TValue>,
+ IReadOnlyDictionary<TKey, TValue>
{
- private readonly Dictionary _objectDict;
+ // 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 unsafe delegate* managed<in TKey, godot_variant> _convertKeyToVariantCallback;
+ private static unsafe delegate* managed<in godot_variant, TKey> _convertKeyToManagedCallback;
+ private static unsafe delegate* managed<in TValue, godot_variant> _convertValueToVariantCallback;
+ private static unsafe delegate* managed<in godot_variant, TValue> _convertValueToManagedCallback;
+
+ // ReSharper restore StaticMemberInGenericType
+
+ static unsafe Dictionary()
+ {
+ _convertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TKey>();
+ _convertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TKey>();
+ _convertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TValue>();
+ _convertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TValue>();
+ }
+
+ private static unsafe void ValidateVariantConversionCallbacks()
+ {
+ if (_convertKeyToVariantCallback == null || _convertKeyToManagedCallback == null)
+ {
+ throw new InvalidOperationException(
+ $"The dictionary key type is not supported for conversion to Variant: '{typeof(TKey).FullName}'");
+ }
+
+ if (_convertValueToVariantCallback == null || _convertValueToManagedCallback == null)
+ {
+ throw new InvalidOperationException(
+ $"The dictionary value type is not supported for conversion to Variant: '{typeof(TValue).FullName}'");
+ }
+ }
- internal static int valTypeEncoding;
- internal static IntPtr valTypeClass;
+ private readonly Dictionary _underlyingDict;
- static Dictionary()
+ internal ref godot_dictionary.movable NativeValue
{
- Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _underlyingDict.NativeValue;
}
/// <summary>
@@ -381,7 +404,9 @@ namespace Godot.Collections
/// </summary>
public Dictionary()
{
- _objectDict = new Dictionary();
+ ValidateVariantConversionCallbacks();
+
+ _underlyingDict = new Dictionary();
}
/// <summary>
@@ -391,19 +416,15 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
- _objectDict = new Dictionary();
+ ValidateVariantConversionCallbacks();
if (dictionary == null)
- throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
-
- // TODO: Can be optimized
+ throw new ArgumentNullException(nameof(dictionary));
- IntPtr godotDictionaryPtr = GetPtr();
+ _underlyingDict = new Dictionary();
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
- {
- Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
- }
+ Add(entry.Key, entry.Value);
}
/// <summary>
@@ -413,18 +434,15 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
- _objectDict = dictionary;
- }
+ ValidateVariantConversionCallbacks();
- internal Dictionary(IntPtr handle)
- {
- _objectDict = new Dictionary(handle);
+ _underlyingDict = dictionary;
}
- internal Dictionary(DictionarySafeHandle handle)
- {
- _objectDict = new Dictionary(handle);
- }
+ // Explicit name to make it very clear
+ internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(
+ godot_dictionary nativeValueToOwn)
+ => new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
@@ -432,12 +450,7 @@ namespace Godot.Collections
/// <param name="from">The typed dictionary to convert.</param>
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
{
- return from._objectDict;
- }
-
- internal IntPtr GetPtr()
- {
- return _objectDict.GetPtr();
+ return from?._underlyingDict;
}
/// <summary>
@@ -447,7 +460,7 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
- return new Dictionary<TKey, TValue>(_objectDict.Duplicate(deep));
+ return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep));
}
// IDictionary<TKey, TValue>
@@ -456,10 +469,32 @@ namespace Godot.Collections
/// Returns the value at the given <paramref name="key"/>.
/// </summary>
/// <value>The value at the given <paramref name="key"/>.</value>
- public TValue this[TKey key]
+ public unsafe TValue this[TKey key]
{
- get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(_objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
- set { _objectDict[key] = value; }
+ get
+ {
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant value).ToBool())
+ {
+ using (value)
+ return _convertValueToManagedCallback(value);
+ }
+ else
+ {
+ throw new KeyNotFoundException();
+ }
+ }
+ set
+ {
+ using var variantKey = _convertKeyToVariantCallback(key);
+ using var variantValue = _convertValueToVariantCallback(value);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_set_value(ref self,
+ variantKey, variantValue);
+ }
}
/// <summary>
@@ -469,8 +504,10 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(_objectDict.GetPtr());
- return new Array<TKey>(new ArraySafeHandle(handle));
+ godot_array keyArray;
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray);
+ return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray);
}
}
@@ -481,15 +518,30 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Values(_objectDict.GetPtr());
- return new Array<TValue>(new ArraySafeHandle(handle));
+ godot_array valuesArray;
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
+ return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
- private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
+ IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
+
+ IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
+
+ private unsafe KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
- Dictionary.godot_icall_Dictionary_KeyValuePairAt_Generic(GetPtr(), index, out object key, out object value, valTypeEncoding, valTypeClass);
- return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
+ out godot_variant key,
+ out godot_variant value);
+ using (key)
+ using (value)
+ {
+ return new KeyValuePair<TKey, TValue>(
+ _convertKeyToManagedCallback(key),
+ _convertValueToManagedCallback(value));
+ }
}
/// <summary>
@@ -498,9 +550,16 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key at which to add the object.</param>
/// <param name="value">The object to add.</param>
- public void Add(TKey key, TValue value)
+ public unsafe void Add(TKey key, TValue value)
{
- _objectDict.Add(key, value);
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
+ throw new ArgumentException("An element with the same key already exists", nameof(key));
+
+ using var variantValue = _convertValueToVariantCallback(value);
+ NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
}
/// <summary>
@@ -508,18 +567,22 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
- public bool ContainsKey(TKey key)
+ public unsafe bool ContainsKey(TKey key)
{
- return _objectDict.Contains(key);
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
}
/// <summary>
/// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
- public bool Remove(TKey key)
+ public unsafe bool Remove(TKey key)
{
- return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
}
/// <summary>
@@ -528,10 +591,16 @@ namespace Godot.Collections
/// <param name="key">The key of the element to get.</param>
/// <param name="value">The value at the given <paramref name="key"/>.</param>
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
- 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_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass);
- value = found ? (TValue)retValue : default;
+ using var variantKey = _convertKeyToVariantCallback(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ value = found ? _convertValueToManagedCallback(retValue) : default;
+
return found;
}
@@ -542,29 +611,33 @@ namespace Godot.Collections
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count
- {
- get { return _objectDict.Count; }
- }
+ public int Count => _underlyingDict.Count;
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
- {
- _objectDict.Add(item.Key, item.Value);
- }
+ => Add(item.Key, item.Value);
/// <summary>
/// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
- public void Clear()
- {
- _objectDict.Clear();
- }
+ public void Clear() => _underlyingDict.Clear();
- bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
+ unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
- return _objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
+ using var variantKey = _convertKeyToVariantCallback(item.Key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ using var variantValue = _convertValueToVariantCallback(item.Value);
+ return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
+ }
}
/// <summary>
@@ -579,12 +652,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++)
{
@@ -593,10 +668,27 @@ namespace Godot.Collections
}
}
- bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
+ unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
- return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
- ;
+ using var variantKey = _convertKeyToVariantCallback(item.Key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ using var variantValue = _convertValueToVariantCallback(item.Value);
+ if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
+ {
+ return NativeFuncs.godotsharp_dictionary_remove_key(
+ ref self, variantKey).ToBool();
+ }
+
+ return false;
+ }
}
// IEnumerable<KeyValuePair<TKey, TValue>>
@@ -613,15 +705,18 @@ namespace Godot.Collections
}
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
/// </summary>
/// <returns>A string representation of this dictionary.</returns>
- public override string ToString() => _objectDict.ToString();
+ public override string ToString() => _underlyingDict.ToString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.CreateFrom(from);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotDictionary<TKey, TValue>();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
index 6475237002..e6cb4171a7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
@@ -1,20 +1,16 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
public static class Dispatcher
{
- /// <summary>
- /// Implements an external instance of GodotTaskScheduler.
- /// </summary>
- /// <returns>A GodotTaskScheduler instance.</returns>
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler();
+ internal static GodotTaskScheduler DefaultGodotTaskScheduler;
- /// <summary>
- /// Initializes the synchronization context as the context of the GodotTaskScheduler.
- /// </summary>
- public static GodotSynchronizationContext SynchronizationContext =>
- godot_icall_DefaultGodotTaskScheduler().Context;
+ internal static void InitializeDefaultGodotTaskScheduler()
+ => DefaultGodotTaskScheduler = new GodotTaskScheduler();
+
+ public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
new file mode 100644
index 0000000000..75793ea446
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+#nullable enable
+
+namespace Godot
+{
+ internal static class DisposablesTracker
+ {
+ [UnmanagedCallersOnly]
+ internal static void OnGodotShuttingDown()
+ {
+ try
+ {
+ OnGodotShuttingDownImpl();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static void OnGodotShuttingDownImpl()
+ {
+ bool isStdoutVerbose;
+
+ try
+ {
+ isStdoutVerbose = OS.IsStdoutVerbose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // OS singleton already disposed. Maybe OnUnloading was called twice.
+ isStdoutVerbose = false;
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Disposing tracked instances...");
+
+ // Dispose Godot Objects first, and only then dispose other disposables
+ // like StringName, NodePath, Godot.Collections.Array/Dictionary, etc.
+ // The Godot Object Dispose() method may need any of the later instances.
+
+ foreach (WeakReference<Object> item in GodotObjectInstances.Keys)
+ {
+ if (item.TryGetTarget(out Object? self))
+ self.Dispose();
+ }
+
+ foreach (WeakReference<IDisposable> item in OtherInstances.Keys)
+ {
+ if (item.TryGetTarget(out IDisposable? self))
+ self.Dispose();
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Finished disposing tracked instances.");
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ private static ConcurrentDictionary<WeakReference<Godot.Object>, byte> GodotObjectInstances { get; } =
+ new();
+
+ private static ConcurrentDictionary<WeakReference<IDisposable>, byte> OtherInstances { get; } =
+ new();
+
+ public static WeakReference<Object> RegisterGodotObject(Object godotObject)
+ {
+ var weakReferenceToSelf = new WeakReference<Object>(godotObject);
+ GodotObjectInstances.TryAdd(weakReferenceToSelf, 0);
+ return weakReferenceToSelf;
+ }
+
+ public static WeakReference<IDisposable> RegisterDisposable(IDisposable disposable)
+ {
+ var weakReferenceToSelf = new WeakReference<IDisposable>(disposable);
+ OtherInstances.TryAdd(weakReferenceToSelf, 0);
+ return weakReferenceToSelf;
+ }
+
+ public static void UnregisterGodotObject(Object godotObject, WeakReference<Object> weakReferenceToSelf)
+ {
+ if (!GodotObjectInstances.TryRemove(weakReferenceToSelf, out _))
+ throw new ArgumentException("Godot Object not registered", nameof(weakReferenceToSelf));
+ }
+
+ public static void UnregisterDisposable(WeakReference<IDisposable> weakReference)
+ {
+ if (!OtherInstances.TryRemove(weakReference, out _))
+ throw new ArgumentException("Disposable not registered", nameof(weakReference));
+ }
+ }
+}
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 26d5f9c796..0000000000
--- 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
-{
- /// <summary>
- /// Represents an <see cref="Object"/> whose members can be dynamically accessed at runtime through the Variant API.
- /// </summary>
- /// <remarks>
- /// <para>
- /// The <see cref="DynamicGodotObject"/> class enables access to the Variant
- /// members of a <see cref="Object"/> instance at runtime.
- /// </para>
- /// <para>
- /// This allows accessing the class members using their original names in the engine as well as the members from the
- /// script attached to the <see cref="Object"/>, regardless of the scripting language it was written in.
- /// </para>
- /// </remarks>
- /// <example>
- /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Object"/>.
- /// <code>
- /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject;
- /// sprite.add_child(this);
- ///
- /// if ((sprite.hframes * sprite.vframes) &gt; 0)
- /// sprite.frame = 0;
- /// </code>
- /// </example>
- /// <example>
- /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Object"/>.
- /// <code>
- /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject;
- ///
- /// if (childNode.print_allowed)
- /// {
- /// childNode.message = "Hello from C#";
- /// childNode.print_message(3);
- /// }
- /// </code>
- /// The <c>ChildNode</c> node has the following GDScript script attached:
- /// <code>
- /// // # ChildNode.gd
- /// // var print_allowed = true
- /// // var message = ""
- /// //
- /// // func print_message(times):
- /// // for i in times:
- /// // print(message)
- /// </code>
- /// </example>
- public class DynamicGodotObject : DynamicObject
- {
- /// <summary>
- /// Gets the <see cref="Object"/> associated with this <see cref="DynamicGodotObject"/>.
- /// </summary>
- public Object Value { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DynamicGodotObject"/> class.
- /// </summary>
- /// <param name="godotObject">
- /// The <see cref="Object"/> that will be associated with this <see cref="DynamicGodotObject"/>.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// Thrown when the <paramref name="godotObject"/> parameter is <see langword="null"/>.
- /// </exception>
- public DynamicGodotObject(Object godotObject)
- {
- if (godotObject == null)
- throw new ArgumentNullException(nameof(godotObject));
-
- Value = godotObject;
- }
-
- /// <inheritdoc/>
- public override IEnumerable<string> GetDynamicMemberNames()
- {
- return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value));
- }
-
- /// <inheritdoc/>
- 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);
- }
-
- /// <inheritdoc/>
- 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);
- }
-
- /// <inheritdoc/>
- 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);
- }
-
- /// <inheritdoc/>
- public override bool TryGetMember(GetMemberBinder binder, out object result)
- {
- return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
- {
- return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result);
- }
-
- /// <inheritdoc/>
- 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);
- }
-
- /// <inheritdoc/>
- 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/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
index 1dc21b6303..df0e839866 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
@@ -93,8 +93,12 @@ namespace Godot
/// Negative indices access the children from the last one.
/// To access a child node via its name, use <see cref="GetNode"/>.
/// </summary>
- /// <seealso cref="GetChildOrNull{T}(int)"/>
+ /// <seealso cref="GetChildOrNull{T}(int, bool)"/>
/// <param name="idx">Child index.</param>
+ /// <param name="includeInternal">
+ /// If <see langword="false"/>, internal children are skipped (see <c>internal</c>
+ /// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>).
+ /// </param>
/// <exception cref="InvalidCastException">
/// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
/// </exception>
@@ -102,9 +106,9 @@ namespace Godot
/// <returns>
/// The child <see cref="Node"/> at the given index <paramref name="idx"/>.
/// </returns>
- public T GetChild<T>(int idx) where T : class
+ public T GetChild<T>(int idx, bool includeInternal = false) where T : class
{
- return (T)(object)GetChild(idx);
+ return (T)(object)GetChild(idx, includeInternal);
}
/// <summary>
@@ -113,15 +117,20 @@ namespace Godot
/// Negative indices access the children from the last one.
/// To access a child node via its name, use <see cref="GetNode"/>.
/// </summary>
- /// <seealso cref="GetChild{T}(int)"/>
+ /// <seealso cref="GetChild{T}(int, bool)"/>
/// <param name="idx">Child index.</param>
+ /// <param name="includeInternal">
+ /// If <see langword="false"/>, internal children are skipped (see <c>internal</c>
+ /// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>).
+ /// </param>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
/// <returns>
/// The child <see cref="Node"/> at the given index <paramref name="idx"/>, or <see langword="null"/> if not found.
/// </returns>
- public T GetChildOrNull<T>(int idx) where T : class
+ public T GetChildOrNull<T>(int idx, bool includeInternal = false) where T : class
{
- return GetChild(idx) as T;
+ int count = GetChildCount(includeInternal);
+ return idx >= -count && idx < count ? GetChild(idx, includeInternal) as T : null;
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
index 691fd85964..4094ceeb22 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,17 @@ namespace Godot
/// </returns>
public static WeakRef WeakRef(Object obj)
{
- return godot_icall_Object_weakref(GetPtr(obj));
- }
+ if (!IsInstanceValid(obj))
+ return null;
+
+ NativeFuncs.godotsharp_weakref(GetPtr(obj), out godot_ref weakRef);
+ using (weakRef)
+ {
+ if (weakRef.IsNull)
+ return null;
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern WeakRef godot_icall_Object_weakref(IntPtr obj);
+ return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef.Reference);
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
deleted file mode 100644
index df130a5c77..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using Godot.Collections;
-
-namespace Godot
-{
- public partial class SceneTree
- {
- /// <summary>
- /// Returns a list of all nodes assigned to the given <paramref name="group"/>.
- /// </summary>
- /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
- public Array<T> GetNodesInGroup<T>(StringName group) where T : class
- {
- return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T)));
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 236d0666bc..9e7da7757a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -1,11 +1,6 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Collections.Generic;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -26,9 +21,11 @@ namespace Godot
/// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param>
/// <param name="allowObjects">If objects should be decoded.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
- public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
+ public static Variant Bytes2Var(Span<byte> bytes, bool allowObjects = false)
{
- return godot_icall_GD_bytes2var(bytes, allowObjects);
+ using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
+ NativeFuncs.godotsharp_bytes2var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
@@ -46,9 +43,10 @@ namespace Godot
/// </code>
/// </example>
/// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns>
- public static object Convert(object what, Variant.Type type)
+ public static Variant Convert(Variant what, Variant.Type type)
{
- return godot_icall_GD_convert(what, type);
+ NativeFuncs.godotsharp_convert((godot_variant)what.NativeVar, (int)type, out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
@@ -62,7 +60,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)
{
@@ -82,9 +80,9 @@ namespace Godot
/// </example>
/// <param name="var">Variable that will be hashed.</param>
/// <returns>Hash of the variable passed.</returns>
- public static int Hash(object var)
+ public static int Hash(Variant var)
{
- return godot_icall_GD_hash(var);
+ return NativeFuncs.godotsharp_hash((godot_variant)var.NativeVar);
}
/// <summary>
@@ -110,7 +108,7 @@ namespace Godot
/// <returns>The <see cref="Object"/> instance.</returns>
public static Object InstanceFromId(ulong instanceId)
{
- return godot_icall_GD_instance_from_id(instanceId);
+ return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
}
/// <summary>
@@ -191,8 +189,6 @@ namespace Godot
/// Pushes an error message to Godot's built-in debugger and to the OS terminal.
///
/// Note: Errors printed this way will not pause project execution.
- /// To print an error message and pause project execution in debug builds,
- /// use [code]assert(false, "test error")[/code] instead.
/// </summary>
/// <example>
/// <code>
@@ -202,7 +198,8 @@ namespace Godot
/// <param name="message">Error message.</param>
public static void PushError(string message)
{
- godot_icall_GD_pusherror(message);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pusherror(godotStr);
}
/// <summary>
@@ -214,7 +211,8 @@ namespace Godot
/// <param name="message">Warning message.</param>
public static void PushWarning(string message)
{
- godot_icall_GD_pushwarning(message);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pushwarning(godotStr);
}
/// <summary>
@@ -235,7 +233,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void Print(params object[] what)
{
- godot_icall_GD_print(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_print(godotStr);
}
/// <summary>
@@ -264,7 +264,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintRich(params object[] what)
{
- godot_icall_GD_print_rich(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_print_rich(godotStr);
}
/// <summary>
@@ -286,7 +288,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintErr(params object[] what)
{
- godot_icall_GD_printerr(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printerr(godotStr);
}
/// <summary>
@@ -306,7 +310,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintRaw(params object[] what)
{
- godot_icall_GD_printraw(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printraw(godotStr);
}
/// <summary>
@@ -320,7 +326,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintS(params object[] what)
{
- godot_icall_GD_prints(GetPrintParams(what));
+ string str = string.Join(' ', GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_prints(godotStr);
}
/// <summary>
@@ -334,7 +342,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintT(params object[] what)
{
- godot_icall_GD_printt(GetPrintParams(what));
+ string str = string.Join('\t', GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printt(godotStr);
}
/// <summary>
@@ -348,7 +358,7 @@ namespace Godot
/// <returns>A random <see langword="float"/> number.</returns>
public static float Randf()
{
- return godot_icall_GD_randf();
+ return NativeFuncs.godotsharp_randf();
}
/// <summary>
@@ -358,7 +368,7 @@ namespace Godot
/// <returns>A random normally-distributed <see langword="float"/> number.</returns>
public static double Randfn(double mean, double deviation)
{
- return godot_icall_GD_randfn(mean, deviation);
+ return NativeFuncs.godotsharp_randfn(mean, deviation);
}
/// <summary>
@@ -376,7 +386,7 @@ namespace Godot
/// <returns>A random <see langword="uint"/> number.</returns>
public static uint Randi()
{
- return godot_icall_GD_randi();
+ return NativeFuncs.godotsharp_randi();
}
/// <summary>
@@ -389,7 +399,7 @@ namespace Godot
/// </summary>
public static void Randomize()
{
- godot_icall_GD_randomize();
+ NativeFuncs.godotsharp_randomize();
}
/// <summary>
@@ -404,7 +414,7 @@ namespace Godot
/// <returns>A random <see langword="double"/> number inside the given range.</returns>
public static double RandRange(double from, double to)
{
- return godot_icall_GD_randf_range(from, to);
+ return NativeFuncs.godotsharp_randf_range(from, to);
}
/// <summary>
@@ -421,7 +431,7 @@ namespace Godot
/// <returns>A random <see langword="int"/> number inside the given range.</returns>
public static int RandRange(int from, int to)
{
- return godot_icall_GD_randi_range(from, to);
+ return NativeFuncs.godotsharp_randi_range(from, to);
}
/// <summary>
@@ -434,7 +444,7 @@ namespace Godot
/// <returns>A random <see langword="uint"/> number.</returns>
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);
}
/// <summary>
@@ -491,7 +501,7 @@ namespace Godot
/// <param name="seed">Seed that will be used.</param>
public static void Seed(ulong seed)
{
- godot_icall_GD_seed(seed);
+ NativeFuncs.godotsharp_seed(seed);
}
/// <summary>
@@ -499,13 +509,16 @@ namespace Godot
/// </summary>
/// <param name="what">Arguments that will converted to string.</param>
/// <returns>The string formed by the given arguments.</returns>
- public static string Str(params object[] what)
+ public static string Str(params Variant[] what)
{
- return godot_icall_GD_str(what);
+ using var whatGodot = new Godot.Collections.Array(what);
+ NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret);
+ using (ret)
+ return Marshaling.ConvertStringToManaged(ret);
}
/// <summary>
- /// Converts a formatted string that was returned by <see cref="Var2Str(object)"/> to the original value.
+ /// Converts a formatted string that was returned by <see cref="Var2Str(Variant)"/> to the original value.
/// </summary>
/// <example>
/// <code>
@@ -516,32 +529,27 @@ namespace Godot
/// </example>
/// <param name="str">String that will be converted to Variant.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
- public static object Str2Var(string str)
+ public static Variant Str2Var(string str)
{
- return godot_icall_GD_str2var(str);
- }
-
- /// <summary>
- /// Returns whether the given class exists in <see cref="ClassDB"/>.
- /// </summary>
- /// <returns>If the class exists in <see cref="ClassDB"/>.</returns>
- public static bool TypeExists(StringName type)
- {
- return godot_icall_GD_type_exists(StringName.GetPtr(type));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_str2var(godotStr, out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
/// Encodes a <c>Variant</c> value to a byte array.
/// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed
/// (and can potentially include code).
- /// Deserialization can be done with <see cref="Bytes2Var(byte[], bool)"/>.
+ /// Deserialization can be done with <see cref="Bytes2Var(Span{byte}, bool)"/>.
/// </summary>
/// <param name="var">Variant that will be encoded.</param>
/// <param name="fullObjects">If objects should be serialized.</param>
/// <returns>The <c>Variant</c> encoded as an array of bytes.</returns>
- public static byte[] Var2Bytes(object var, bool fullObjects = false)
+ public static byte[] Var2Bytes(Variant var, bool fullObjects = false)
{
- return godot_icall_GD_var2bytes(var, fullObjects);
+ NativeFuncs.godotsharp_var2bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes);
+ using (varBytes)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
}
/// <summary>
@@ -561,9 +569,11 @@ namespace Godot
/// </example>
/// <param name="var">Variant that will be converted to string.</param>
/// <returns>The <c>Variant</c> encoded as a string.</returns>
- public static string Var2Str(object var)
+ public static string Var2Str(Variant var)
{
- return godot_icall_GD_var2str(var);
+ NativeFuncs.godotsharp_var2str((godot_variant)var.NativeVar, out godot_string ret);
+ using (ret)
+ return Marshaling.ConvertStringToManaged(ret);
}
/// <summary>
@@ -572,85 +582,7 @@ namespace Godot
/// <returns>The <see cref="Variant.Type"/> for the given <paramref name="type"/>.</returns>
public static Variant.Type TypeToVariantType(Type type)
{
- return godot_icall_TypeToVariantType(type);
+ return Marshaling.ConvertManagedTypeToVariantType(type, out bool _);
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_GD_bytes2var(byte[] 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(IntPtr type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
-
- [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/GodotTraceListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
index 9ccac1faaf..78a9d0fe9d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
@@ -17,10 +17,7 @@ namespace Godot
public override void Fail(string message, string detailMessage)
{
GD.PrintErr("Assertion failed: ", message);
- if (detailMessage != null)
- {
- GD.PrintErr(" Details: ", detailMessage);
- }
+ GD.PrintErr(" Details: ", detailMessage);
try
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
index 729529d093..a17741ddeb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
@@ -1,17 +1,33 @@
using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
public static partial class GD
{
- /// <summary>
- /// Fires when an unhandled exception occurs, regardless of project settings.
- /// </summary>
- public static event EventHandler<UnhandledExceptionArgs> UnhandledException;
-
- private static void OnUnhandledException(Exception e)
+ [UnmanagedCallersOnly]
+ internal static void OnCoreApiAssemblyLoaded(godot_bool isDebug)
{
- UnhandledException?.Invoke(null, new UnhandledExceptionArgs(e));
+ try
+ {
+ Dispatcher.InitializeDefaultGodotTaskScheduler();
+
+ if (isDebug.ToBool())
+ {
+ DebuggingUtils.InstallTraceListener();
+
+ AppDomain.CurrentDomain.UnhandledException += (_, e) =>
+ {
+ // Exception.ToString() includes the inner exception
+ ExceptionUtils.LogUnhandledException((Exception)e.ExceptionObject);
+ };
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
deleted file mode 100644
index 50ae2eb112..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Godot
-{
- internal static class MarshalUtils
- {
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericArray(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Collections.Array<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="List{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsSystemGenericList(Type type) =>
- type.GetGenericTypeDefinition() == typeof(List<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsSystemGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Dictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="IEnumerable{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="ICollection{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="IDictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type.
- /// </summary>
- private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false);
-
- /// <summary>
- /// Returns the generic type definition of <paramref name="type"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition)
- {
- genericTypeDefinition = type.GetGenericTypeDefinition();
- }
-
- /// <summary>
- /// Gets the element type for the given <paramref name="arrayType"/>.
- /// </summary>
- /// <param name="arrayType">Type for the generic array.</param>
- /// <param name="elementType">Element type for the generic array.</param>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="arrayType"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void ArrayGetElementType(Type arrayType, out Type elementType)
- {
- elementType = arrayType.GetGenericArguments()[0];
- }
-
- /// <summary>
- /// Gets the key type and the value type for the given <paramref name="dictionaryType"/>.
- /// </summary>
- /// <param name="dictionaryType">The type for the generic dictionary.</param>
- /// <param name="keyType">Key type for the generic dictionary.</param>
- /// <param name="valueType">Value type for the generic dictionary.</param>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="dictionaryType"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
- {
- var genericArgs = dictionaryType.GetGenericArguments();
- keyType = genericArgs[0];
- valueType = genericArgs[1];
- }
-
- /// <summary>
- /// Constructs a new <see cref="Type"/> from <see cref="Collections.Array{T}"/>
- /// where the generic type for the elements is <paramref name="elemType"/>.
- /// </summary>
- /// <param name="elemType">Element type for the array.</param>
- /// <returns>The generic array type with the specified element type.</returns>
- private static Type MakeGenericArrayType(Type elemType)
- {
- return typeof(Collections.Array<>).MakeGenericType(elemType);
- }
-
- /// <summary>
- /// Constructs a new <see cref="Type"/> from <see cref="Collections.Dictionary{TKey, TValue}"/>
- /// where the generic type for the keys is <paramref name="keyType"/> and
- /// for the values is <paramref name="valueType"/>.
- /// </summary>
- /// <param name="keyType">Key type for the dictionary.</param>
- /// <param name="valueType">Key type for the dictionary.</param>
- /// <returns>The generic dictionary type with the specified key and value types.</returns>
- private static Type MakeGenericDictionaryType(Type keyType, Type valueType)
- {
- return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 36b7d0f80f..41a0dd871c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
namespace Godot
@@ -759,9 +754,10 @@ namespace Godot
}
/// <summary>
- /// Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code].
- /// If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave).
- /// If [code]length[/code] is less than zero, it becomes positive.
+ /// Returns the <paramref name="value"/> wrapped between <c>0</c> and the <paramref name="length"/>.
+ /// If the limit is reached, the next value the function returned is decreased to the <c>0</c> side
+ /// or increased to the <paramref name="length"/> side (like a triangle wave).
+ /// If <paramref name="length"/> is less than zero, it becomes positive.
/// </summary>
/// <param name="value">The value to pingpong.</param>
/// <param name="length">The maximum value of the function.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index f15d01b34b..ea05c1547c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
namespace Godot
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
new file mode 100644
index 0000000000..afef20a7f2
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
@@ -0,0 +1,313 @@
+using System.Runtime.CompilerServices;
+
+namespace Godot.NativeInterop;
+
+// Ref structs are not allowed as generic type parameters, so we can't use Unsafe.AsPointer<T>/AsRef<T>.
+// As a workaround we create our own overloads for our structs with some tricks under the hood.
+
+public static class CustomUnsafe
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_ref* AsPointer(ref godot_ref value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_ref* ReadOnlyRefAsPointer(in godot_ref value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_ref AsRef(godot_ref* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_ref AsRef(in godot_ref source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant_call_error* AsPointer(ref godot_variant_call_error value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant_call_error* ReadOnlyRefAsPointer(in godot_variant_call_error value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant_call_error AsRef(godot_variant_call_error* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant_call_error AsRef(in godot_variant_call_error source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant* AsPointer(ref godot_variant value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant* ReadOnlyRefAsPointer(in godot_variant value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant AsRef(godot_variant* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant AsRef(in godot_variant source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string* AsPointer(ref godot_string value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string* ReadOnlyRefAsPointer(in godot_string value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string AsRef(godot_string* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string AsRef(in godot_string source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string_name* AsPointer(ref godot_string_name value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string_name* ReadOnlyRefAsPointer(in godot_string_name value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string_name AsRef(godot_string_name* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string_name AsRef(in godot_string_name source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_node_path* AsPointer(ref godot_node_path value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_node_path* ReadOnlyRefAsPointer(in godot_node_path value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_node_path AsRef(godot_node_path* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_node_path AsRef(in godot_node_path source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_signal* AsPointer(ref godot_signal value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_signal* ReadOnlyRefAsPointer(in godot_signal value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_signal AsRef(godot_signal* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_signal AsRef(in godot_signal source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_callable* AsPointer(ref godot_callable value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_callable* ReadOnlyRefAsPointer(in godot_callable value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_callable AsRef(godot_callable* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_callable AsRef(in godot_callable source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_array* AsPointer(ref godot_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_array* ReadOnlyRefAsPointer(in godot_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_array AsRef(godot_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_array AsRef(in godot_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_dictionary* AsPointer(ref godot_dictionary value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_dictionary* ReadOnlyRefAsPointer(in godot_dictionary value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_dictionary AsRef(godot_dictionary* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_dictionary AsRef(in godot_dictionary source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_byte_array* AsPointer(ref godot_packed_byte_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_byte_array* ReadOnlyRefAsPointer(in godot_packed_byte_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_byte_array AsRef(godot_packed_byte_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_byte_array AsRef(in godot_packed_byte_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int32_array* AsPointer(ref godot_packed_int32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int32_array* ReadOnlyRefAsPointer(in godot_packed_int32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int32_array AsRef(godot_packed_int32_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int32_array AsRef(in godot_packed_int32_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int64_array* AsPointer(ref godot_packed_int64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int64_array* ReadOnlyRefAsPointer(in godot_packed_int64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int64_array AsRef(godot_packed_int64_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int64_array AsRef(in godot_packed_int64_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float32_array* AsPointer(ref godot_packed_float32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float32_array* ReadOnlyRefAsPointer(in godot_packed_float32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float32_array AsRef(godot_packed_float32_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float32_array AsRef(in godot_packed_float32_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float64_array* AsPointer(ref godot_packed_float64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float64_array* ReadOnlyRefAsPointer(in godot_packed_float64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float64_array AsRef(godot_packed_float64_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float64_array AsRef(in godot_packed_float64_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_string_array* AsPointer(ref godot_packed_string_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_string_array* ReadOnlyRefAsPointer(in godot_packed_string_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_string_array AsRef(godot_packed_string_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_string_array AsRef(in godot_packed_string_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector2_array* AsPointer(ref godot_packed_vector2_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector2_array* ReadOnlyRefAsPointer(in godot_packed_vector2_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector2_array AsRef(godot_packed_vector2_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector2_array AsRef(in godot_packed_vector2_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector3_array* AsPointer(ref godot_packed_vector3_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector3_array* ReadOnlyRefAsPointer(in godot_packed_vector3_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector3_array AsRef(godot_packed_vector3_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector3_array AsRef(in godot_packed_vector3_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_color_array* AsPointer(ref godot_packed_color_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_color_array* ReadOnlyRefAsPointer(in godot_packed_color_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_color_array AsRef(godot_packed_color_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_color_array AsRef(in godot_packed_color_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
new file mode 100644
index 0000000000..5a0ea2ba13
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ internal static class ExceptionUtils
+ {
+ public static void PushError(string message)
+ {
+ GD.PushError(message);
+ }
+
+ private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog)
+ {
+ try
+ {
+ // This better not throw
+ PushError(string.Concat("Exception thrown while trying to log another exception...",
+ "\n### Exception ###\n", exceptionToLog.ToString(),
+ "\n### Logger exception ###\n", loggerException.ToString()));
+ }
+ catch (Exception)
+ {
+ // Well, too bad...
+ }
+ }
+
+ private record struct StackInfoTuple(string? File, string Func, int Line);
+
+ private static void CollectExceptionInfo(Exception exception, List<StackInfoTuple> globalFrames,
+ StringBuilder excMsg)
+ {
+ if (excMsg.Length > 0)
+ excMsg.Append(" ---> ");
+ excMsg.Append(exception.GetType().FullName);
+ excMsg.Append(": ");
+ excMsg.Append(exception.Message);
+
+ var innerExc = exception.InnerException;
+
+ if (innerExc != null)
+ {
+ CollectExceptionInfo(innerExc, globalFrames, excMsg);
+ globalFrames.Add(new("", "--- End of inner exception stack trace ---", 0));
+ }
+
+ var stackTrace = new StackTrace(exception, fNeedFileInfo: true);
+
+ foreach (StackFrame frame in stackTrace.GetFrames())
+ {
+ DebuggingUtils.GetStackFrameMethodDecl(frame, out string methodDecl);
+ globalFrames.Add(new(frame.GetFileName(), methodDecl, frame.GetFileLineNumber()));
+ }
+ }
+
+ private static void SendToScriptDebugger(Exception e)
+ {
+ var globalFrames = new List<StackInfoTuple>();
+
+ var excMsg = new StringBuilder();
+
+ CollectExceptionInfo(e, globalFrames, excMsg);
+
+ string file = globalFrames.Count > 0 ? globalFrames[0].File ?? "" : "";
+ string func = globalFrames.Count > 0 ? globalFrames[0].Func : "";
+ int line = globalFrames.Count > 0 ? globalFrames[0].Line : 0;
+ string errorMsg = "Exception";
+
+ using godot_string nFile = Marshaling.ConvertStringToNative(file);
+ using godot_string nFunc = Marshaling.ConvertStringToNative(func);
+ using godot_string nErrorMsg = Marshaling.ConvertStringToNative(errorMsg);
+ using godot_string nExcMsg = Marshaling.ConvertStringToNative(excMsg.ToString());
+
+ using DebuggingUtils.godot_stack_info_vector stackInfoVector = default;
+
+ stackInfoVector.Resize(globalFrames.Count);
+
+ unsafe
+ {
+ for (int i = 0; i < globalFrames.Count; i++)
+ {
+ DebuggingUtils.godot_stack_info* stackInfo = &stackInfoVector.Elements[i];
+
+ var globalFrame = globalFrames[i];
+
+ // Assign directly to element in Vector. This way we don't need to worry
+ // about disposal if an exception is thrown. The Vector takes care of it.
+ stackInfo->File = Marshaling.ConvertStringToNative(globalFrame.File);
+ stackInfo->Func = Marshaling.ConvertStringToNative(globalFrame.Func);
+ stackInfo->Line = globalFrame.Line;
+ }
+
+ NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line,
+ nErrorMsg, nExcMsg, p_warning: godot_bool.False, stackInfoVector);
+ }
+ }
+
+ public static void LogException(Exception e)
+ {
+ try
+ {
+ if (NativeFuncs.godotsharp_internal_script_debugger_is_active())
+ {
+ SendToScriptDebugger(e);
+ }
+ else
+ {
+ GD.PushError(e.ToString());
+ }
+ }
+ catch (Exception unexpected)
+ {
+ OnExceptionLoggerException(unexpected, e);
+ }
+ }
+
+ public static void LogUnhandledException(Exception e)
+ {
+ try
+ {
+ if (NativeFuncs.godotsharp_internal_script_debugger_is_active())
+ {
+ SendToScriptDebugger(e);
+ }
+
+ // In this case, print it as well in addition to sending it to the script debugger
+ GD.PushError("Unhandled exception\n" + e);
+ }
+ catch (Exception unexpected)
+ {
+ OnExceptionLoggerException(unexpected, e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs
new file mode 100644
index 0000000000..5579992d2b
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public class GodotDllImportResolver
+ {
+ private IntPtr _internalHandle;
+
+ public GodotDllImportResolver(IntPtr internalHandle)
+ {
+ _internalHandle = internalHandle;
+ }
+
+ public IntPtr OnResolveDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ if (libraryName == "__Internal")
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Win32.GetModuleHandle(IntPtr.Zero);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return _internalHandle;
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return MacOS.dlopen(IntPtr.Zero, MacOS.RTLD_LAZY);
+ }
+ }
+
+ return IntPtr.Zero;
+ }
+
+ // ReSharper disable InconsistentNaming
+ private static class MacOS
+ {
+ private const string SystemLibrary = "/usr/lib/libSystem.dylib";
+
+ public const int RTLD_LAZY = 1;
+
+ [DllImport(SystemLibrary)]
+ public static extern IntPtr dlopen(IntPtr path, int mode);
+ }
+
+ private static class Win32
+ {
+ private const string SystemLibrary = "Kernel32.dll";
+
+ [DllImport(SystemLibrary)]
+ public static extern IntPtr GetModuleHandle(IntPtr lpModuleName);
+ }
+ // ReSharper restore InconsistentNaming
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
new file mode 100644
index 0000000000..44806e8ecf
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -0,0 +1,1091 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot.NativeInterop
+{
+ // NOTES:
+ // ref structs cannot implement interfaces, but they still work in `using` directives if they declare Dispose()
+
+ public static class GodotBoolExtensions
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_bool ToGodotBool(this bool @bool)
+ {
+ return *(godot_bool*)&@bool;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe bool ToBool(this godot_bool godotBool)
+ {
+ return *(bool*)&godotBool;
+ }
+ }
+
+ // Apparently a struct with a byte is not blittable? It crashes when calling a UnmanagedCallersOnly function ptr.
+ // ReSharper disable once InconsistentNaming
+ public enum godot_bool : byte
+ {
+ True = 1,
+ False = 0
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_ref
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_ref* GetUnsafeAddress()
+ => (godot_ref*)Unsafe.AsPointer(ref Unsafe.AsRef(in _reference));
+
+ private IntPtr _reference;
+
+ public void Dispose()
+ {
+ if (_reference == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_ref_destroy(ref this);
+ _reference = IntPtr.Zero;
+ }
+
+ public readonly IntPtr Reference
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _reference;
+ }
+
+ public readonly bool IsNull
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _reference == IntPtr.Zero;
+ }
+ }
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public 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
+ public ref struct godot_variant_call_error
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_variant_call_error* GetUnsafeAddress()
+ => (godot_variant_call_error*)Unsafe.AsPointer(ref Unsafe.AsRef(in error));
+
+ private godot_variant_call_error_error error;
+ private int argument;
+ private int expected;
+
+ public godot_variant_call_error_error Error
+ {
+ readonly get => error;
+ set => error = value;
+ }
+
+ public int Argument
+ {
+ readonly get => argument;
+ set => argument = value;
+ }
+
+ public Godot.Variant.Type Expected
+ {
+ readonly get => (Godot.Variant.Type)expected;
+ set => expected = (int)value;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_variant
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_variant* GetUnsafeAddress()
+ => (godot_variant*)Unsafe.AsPointer(ref Unsafe.AsRef(in _typeField));
+
+ // 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)] private godot_variant_data _data;
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ private unsafe ref 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
+ public struct godot_variant_obj_data
+ {
+ public ulong id;
+ public IntPtr obj;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public 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 Variant.Type Type
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => (Variant.Type)_typeField;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _typeField = (int)value;
+ }
+
+ public godot_bool Bool
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._bool;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._bool = value;
+ }
+
+ public long Int
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._int;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._int = value;
+ }
+
+ public double Float
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._float;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._float = value;
+ }
+
+ public readonly unsafe Transform2D* Transform2D
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._transform2D;
+ }
+
+ public readonly unsafe AABB* AABB
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._aabb;
+ }
+
+ public readonly unsafe Basis* Basis
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._basis;
+ }
+
+ public readonly unsafe Transform3D* Transform3D
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._transform3D;
+ }
+
+ public readonly unsafe Vector4* Vector4
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._vector4;
+ }
+
+ public readonly unsafe Vector4i* Vector4i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._vector4i;
+ }
+
+ public readonly unsafe Projection* Projection
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._projection;
+ }
+
+ public godot_string_name StringName
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_string_name;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_string_name = value;
+ }
+
+ public godot_string String
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_string;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_string = value;
+ }
+
+ public Vector3 Vector3
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector3;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector3 = value;
+ }
+
+ public Vector3i Vector3i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector3i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector3i = value;
+ }
+
+ public Vector2 Vector2
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector2 = value;
+ }
+
+ public Vector2i Vector2i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector2i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector2i = value;
+ }
+
+ public Rect2 Rect2
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rect2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rect2 = value;
+ }
+
+ public Rect2i Rect2i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rect2i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rect2i = value;
+ }
+
+ public Plane Plane
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_plane;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_plane = value;
+ }
+
+ public Quaternion Quaternion
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_quaternion;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_quaternion = value;
+ }
+
+ public Color Color
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_color;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_color = value;
+ }
+
+ public godot_node_path NodePath
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_node_path;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_node_path = value;
+ }
+
+ public RID RID
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rid;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rid = value;
+ }
+
+ public godot_callable Callable
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_callable;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_callable = value;
+ }
+
+ public godot_signal Signal
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_signal;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_signal = value;
+ }
+
+ public godot_dictionary Dictionary
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_dictionary;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_dictionary = value;
+ }
+
+ public godot_array Array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_array;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_array = value;
+ }
+
+ public readonly IntPtr Object
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._m_obj_data.obj;
+ }
+
+ 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.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ // 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)] private godot_variant_data.godot_variant_data_mem _data;
+
+ public static unsafe explicit operator movable(in godot_variant value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_variant(movable value)
+ => *(godot_variant*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_variant DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_variant*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_string
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_string* GetUnsafeAddress()
+ => (godot_string*)Unsafe.AsPointer(ref Unsafe.AsRef(in _ptr));
+
+ private IntPtr _ptr;
+
+ public void Dispose()
+ {
+ if (_ptr == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_string_destroy(ref this);
+ _ptr = IntPtr.Zero;
+ }
+
+ public readonly IntPtr Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ // Size including the null termination character
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_string_name
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_string_name* GetUnsafeAddress()
+ => (godot_string_name*)Unsafe.AsPointer(ref Unsafe.AsRef(in _data));
+
+ private IntPtr _data;
+
+ public void Dispose()
+ {
+ if (_data == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_string_name_destroy(ref this);
+ _data = IntPtr.Zero;
+ }
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data != IntPtr.Zero;
+ }
+
+ public readonly bool IsEmpty
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++.
+ get => _data == IntPtr.Zero;
+ }
+
+ public static bool operator ==(godot_string_name left, godot_string_name right)
+ {
+ return left._data == right._data;
+ }
+
+ public static bool operator !=(godot_string_name left, godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public bool Equals(godot_string_name other)
+ {
+ return _data == other._data;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is StringName s && s.Equals(this);
+ }
+
+ public override int GetHashCode()
+ {
+ return _data.GetHashCode();
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _data;
+
+ public static unsafe explicit operator movable(in godot_string_name value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_string_name(movable value)
+ => *(godot_string_name*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_string_name DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_string_name*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_node_path
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_node_path* GetUnsafeAddress()
+ => (godot_node_path*)Unsafe.AsPointer(ref Unsafe.AsRef(in _data));
+
+ private IntPtr _data;
+
+ public void Dispose()
+ {
+ if (_data == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_node_path_destroy(ref this);
+ _data = IntPtr.Zero;
+ }
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data != IntPtr.Zero;
+ }
+
+ public readonly bool IsEmpty
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does.
+ get => _data == IntPtr.Zero;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _data;
+
+ public static unsafe explicit operator movable(in godot_node_path value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_node_path(movable value)
+ => *(godot_node_path*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_node_path DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_node_path*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_signal
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_signal* GetUnsafeAddress()
+ => (godot_signal*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private godot_string_name _name;
+
+ // There's padding here on 32-bit
+
+ [FieldOffset(8)] private ulong _objectId;
+
+ public godot_signal(godot_string_name name, ulong objectId) : this()
+ {
+ _name = name;
+ _objectId = objectId;
+ }
+
+ public godot_string_name Name
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _name;
+ }
+
+ public ulong ObjectId
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _objectId;
+ }
+
+ public void Dispose()
+ {
+ if (!_name.IsAllocated)
+ return;
+ NativeFuncs.godotsharp_signal_destroy(ref this);
+ _name = default;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_callable
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_callable* GetUnsafeAddress()
+ => (godot_callable*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private godot_string_name _method;
+
+ // There's padding here on 32-bit
+
+ // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
+ [FieldOffset(8)] private ulong _objectId;
+ [FieldOffset(8)] private IntPtr _custom;
+
+ public godot_callable(godot_string_name method, ulong objectId) : this()
+ {
+ _method = method;
+ _objectId = objectId;
+ }
+
+ public void Dispose()
+ {
+ // _custom needs freeing as well
+ if (!_method.IsAllocated && _custom == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_callable_destroy(ref this);
+ _method = default;
+ _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.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_array* GetUnsafeAddress()
+ => (godot_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private unsafe ArrayPrivate* _p;
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct ArrayPrivate
+ {
+ private uint _safeRefCount;
+
+ public VariantVector _arrayVector;
+ // There are more fields here, but we don't care as we never store this in C#
+
+ public readonly int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _arrayVector.Size;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct VariantVector
+ {
+ private IntPtr _writeProxy;
+ public unsafe godot_variant* _ptr;
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ public readonly unsafe godot_variant* Elements
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p->_arrayVector._ptr;
+ }
+
+ public readonly unsafe bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != null;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != null ? _p->Size : 0;
+ }
+
+ public unsafe void Dispose()
+ {
+ if (_p == null)
+ return;
+ NativeFuncs.godotsharp_array_destroy(ref this);
+ _p = null;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private unsafe ArrayPrivate* _p;
+
+ public static unsafe explicit operator movable(in godot_array value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_array(movable value)
+ => *(godot_array*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_array DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_array*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ // 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
+ public ref struct godot_dictionary
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_dictionary* GetUnsafeAddress()
+ => (godot_dictionary*)Unsafe.AsPointer(ref Unsafe.AsRef(in _p));
+
+ private IntPtr _p;
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != IntPtr.Zero;
+ }
+
+ 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 movable
+ {
+ private IntPtr _p;
+
+ public static unsafe explicit operator movable(in godot_dictionary value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_dictionary(movable value)
+ => *(godot_dictionary*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_dictionary DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_dictionary*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_byte_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_byte_array* GetUnsafeAddress()
+ => (godot_packed_byte_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe byte* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_byte_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe byte* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_int32_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_int32_array* GetUnsafeAddress()
+ => (godot_packed_int32_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe int* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_int32_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe int* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *(_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_int64_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_int64_array* GetUnsafeAddress()
+ => (godot_packed_int64_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe long* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_int64_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe long* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_float32_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_float32_array* GetUnsafeAddress()
+ => (godot_packed_float32_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe float* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_float32_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe float* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_float64_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_float64_array* GetUnsafeAddress()
+ => (godot_packed_float64_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe double* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_float64_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe double* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_string_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_string_array* GetUnsafeAddress()
+ => (godot_packed_string_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe godot_string* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_string_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe godot_string* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_vector2_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_vector2_array* GetUnsafeAddress()
+ => (godot_packed_vector2_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Vector2* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Vector2* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_vector3_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_vector3_array* GetUnsafeAddress()
+ => (godot_packed_vector3_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Vector3* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Vector3* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_color_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_color_array* GetUnsafeAddress()
+ => (godot_packed_color_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Color* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_color_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Color* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs
new file mode 100644
index 0000000000..82f1c04d40
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ internal static class InteropUtils
+ {
+ public static Object UnmanagedGetManaged(IntPtr unmanaged)
+ {
+ // The native pointer may be null
+ if (unmanaged == IntPtr.Zero)
+ return null;
+
+ IntPtr gcHandlePtr;
+ godot_bool hasCsScriptInstance;
+
+ // First try to get the tied managed instance from a CSharpInstance script instance
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed(
+ unmanaged, out hasCsScriptInstance);
+
+ if (gcHandlePtr != IntPtr.Zero)
+ return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
+
+ // Otherwise, if the object has a CSharpInstance script instance, return null
+
+ if (hasCsScriptInstance.ToBool())
+ return null;
+
+ // If it doesn't have a CSharpInstance script instance, try with native instance bindings
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged);
+
+ object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null;
+
+ if (target != null)
+ return (Object)target;
+
+ // If the native instance binding GC handle target was collected, create a new one
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed(
+ unmanaged, gcHandlePtr);
+
+ return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null;
+ }
+
+ public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged,
+ StringName nativeName, bool refCounted, Type type, Type nativeType)
+ {
+ var gcHandle = refCounted ?
+ CustomGCHandle.AllocWeak(managed) :
+ CustomGCHandle.AllocStrong(managed, type);
+
+ if (type == nativeType)
+ {
+ var nativeNameSelf = (godot_string_name)nativeName.NativeValue;
+ NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged(
+ GCHandle.ToIntPtr(gcHandle), unmanaged, nativeNameSelf, refCounted.ToGodotBool());
+ }
+ else
+ {
+ unsafe
+ {
+ // We don't dispose `script` ourselves here.
+ // `tie_user_managed_to_unmanaged` does it for us to avoid another P/Invoke call.
+ godot_ref script;
+ ScriptManagerBridge.GetOrLoadOrCreateScriptForType(type, &script);
+
+ // IMPORTANT: This must be called after GetOrCreateScriptBridgeForType
+ NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged(
+ GCHandle.ToIntPtr(gcHandle), unmanaged, &script, refCounted.ToGodotBool());
+ }
+ }
+ }
+
+ public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged,
+ Type type, Type nativeType)
+ {
+ if (type == nativeType)
+ return;
+
+ var strongGCHandle = CustomGCHandle.AllocStrong(managed);
+ NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
+ GCHandle.ToIntPtr(strongGCHandle), unmanaged);
+ }
+
+ public static Object EngineGetSingleton(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(src));
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
new file mode 100644
index 0000000000..eee19aea46
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -0,0 +1,1092 @@
+using System;
+using System.Runtime.InteropServices;
+
+// ReSharper disable InconsistentNaming
+
+// We want to use full name qualifiers here even if redundant for clarity
+// ReSharper disable RedundantNameQualifier
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public static class Marshaling
+ {
+ internal static Variant.Type ConvertManagedTypeToVariantType(Type type, out bool r_nil_is_variant)
+ {
+ r_nil_is_variant = false;
+
+ 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(int[]))
+ return Variant.Type.PackedInt32Array;
+
+ if (type == typeof(long[]))
+ 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 (type == typeof(StringName[]))
+ return Variant.Type.Array;
+
+ if (type == typeof(NodePath[]))
+ return Variant.Type.Array;
+
+ if (type == typeof(RID[]))
+ return Variant.Type.Array;
+
+ if (typeof(Godot.Object[]).IsAssignableFrom(type))
+ return Variant.Type.Array;
+ }
+ else if (type.IsGenericType)
+ {
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Variant.Type.Object;
+ }
+ else if (type == typeof(Variant))
+ {
+ 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)
+ return Variant.Type.Dictionary;
+
+ if (typeof(Collections.Array) == type)
+ return Variant.Type.Array;
+ }
+
+ break;
+ }
+ }
+
+ // Unknown
+ return Variant.Type.Nil;
+ }
+
+ /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */
+ public static godot_variant ConvertManagedObjectToVariant(object? p_obj)
+ {
+ 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 short @int16:
+ return VariantUtils.CreateFromInt(@int16);
+ case int @int32:
+ return VariantUtils.CreateFromInt(@int32);
+ case long @int64:
+ return VariantUtils.CreateFromInt(@int64);
+ case byte @uint8:
+ return VariantUtils.CreateFromInt(@uint8);
+ case ushort @uint16:
+ return VariantUtils.CreateFromInt(@uint16);
+ case uint @uint32:
+ return VariantUtils.CreateFromInt(@uint32);
+ case ulong @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.CreateFromCallable(@callable);
+ case SignalInfo @signalInfo:
+ return VariantUtils.CreateFromSignalInfo(@signalInfo);
+ case Enum @enum:
+ return VariantUtils.CreateFromInt(Convert.ToInt64(@enum));
+ case string @string:
+ return VariantUtils.CreateFromString(@string);
+ case byte[] byteArray:
+ return VariantUtils.CreateFromPackedByteArray(byteArray);
+ case int[] int32Array:
+ return VariantUtils.CreateFromPackedInt32Array(int32Array);
+ case long[] int64Array:
+ return VariantUtils.CreateFromPackedInt64Array(int64Array);
+ case float[] floatArray:
+ return VariantUtils.CreateFromPackedFloat32Array(floatArray);
+ case double[] doubleArray:
+ return VariantUtils.CreateFromPackedFloat64Array(doubleArray);
+ case string[] stringArray:
+ return VariantUtils.CreateFromPackedStringArray(stringArray);
+ case Vector2[] vector2Array:
+ return VariantUtils.CreateFromPackedVector2Array(vector2Array);
+ case Vector3[] vector3Array:
+ return VariantUtils.CreateFromPackedVector3Array(vector3Array);
+ case Color[] colorArray:
+ return VariantUtils.CreateFromPackedColorArray(colorArray);
+ case StringName[] stringNameArray:
+ return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
+ case NodePath[] nodePathArray:
+ return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
+ case RID[] ridArray:
+ return VariantUtils.CreateFromSystemArrayOfRID(ridArray);
+ case Godot.Object[] godotObjectArray:
+ return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray);
+ case Godot.Object godotObject:
+ return VariantUtils.CreateFromGodotObject(godotObject);
+ case StringName stringName:
+ return VariantUtils.CreateFromStringName(stringName);
+ case NodePath nodePath:
+ return VariantUtils.CreateFromNodePath(nodePath);
+ case RID rid:
+ return VariantUtils.CreateFromRID(rid);
+ case Collections.Dictionary godotDictionary:
+ return VariantUtils.CreateFromDictionary(godotDictionary);
+ case Collections.Array godotArray:
+ return VariantUtils.CreateFromArray(godotArray);
+ case Variant variant:
+ return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
+ }
+
+ GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
+ p_obj.GetType().FullName + ".");
+ return new godot_variant();
+ }
+
+ public static object? ConvertVariantToManagedObjectOfType(in 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 VariantUtils.ConvertToStringObject(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))
+ return VariantUtils.ConvertToCallableManaged(p_var);
+
+ if (type == typeof(SignalInfo))
+ return VariantUtils.ConvertToSignalInfo(p_var);
+
+ 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 ConvertVariantToSystemArrayOfType(p_var, type);
+ }
+ else if (type.IsGenericType)
+ {
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ {
+ var godotObject = VariantUtils.ConvertToGodotObject(p_var);
+
+ if (!type.IsInstanceOfType(godotObject))
+ {
+ GD.PushError("Invalid cast when marshaling Godot.Object type." +
+ $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
+ return null;
+ }
+
+ return godotObject;
+ }
+
+ return null;
+ }
+ else if (type == typeof(Variant))
+ {
+ return Variant.CreateCopyingBorrowed(p_var);
+ }
+
+ if (ConvertVariantToManagedObjectOfClass(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 object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type)
+ {
+ if (type == typeof(byte[]))
+ return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
+
+ if (type == typeof(int[]))
+ return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
+
+ if (type == typeof(long[]))
+ return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
+
+ if (type == typeof(float[]))
+ return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
+
+ if (type == typeof(double[]))
+ return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
+
+ if (type == typeof(string[]))
+ return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
+
+ if (type == typeof(Vector2[]))
+ return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
+
+ if (type == typeof(Vector3[]))
+ return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
+
+ if (type == typeof(Color[]))
+ return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
+
+ if (type == typeof(StringName[]))
+ return VariantUtils.ConvertToSystemArrayOfStringName(p_var);
+
+ if (type == typeof(NodePath[]))
+ return VariantUtils.ConvertToSystemArrayOfNodePath(p_var);
+
+ if (type == typeof(RID[]))
+ return VariantUtils.ConvertToSystemArrayOfRID(p_var);
+
+ if (typeof(Godot.Object[]).IsAssignableFrom(type))
+ return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type);
+
+ GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " +
+ type.GetElementType()!.FullName + ".");
+ return null;
+ }
+
+ private static bool ConvertVariantToManagedObjectOfClass(in godot_variant p_var, Type type,
+ out object? res)
+ {
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ {
+ if (p_var.Type == Variant.Type.Nil)
+ {
+ res = null;
+ return true;
+ }
+
+ if (p_var.Type != Variant.Type.Object)
+ {
+ GD.PushError("Invalid cast when marshaling Godot.Object type." +
+ $" Variant type is `{p_var.Type}`; expected `{p_var.Object}`.");
+ res = null;
+ return true;
+ }
+
+ var godotObjectPtr = VariantUtils.ConvertToGodotObjectPtr(p_var);
+
+ if (godotObjectPtr == IntPtr.Zero)
+ {
+ res = null;
+ return true;
+ }
+
+ var godotObject = InteropUtils.UnmanagedGetManaged(godotObjectPtr);
+
+ if (!type.IsInstanceOfType(godotObject))
+ {
+ GD.PushError("Invalid cast when marshaling Godot.Object type." +
+ $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`.");
+ res = null;
+ return false;
+ }
+
+ res = godotObject;
+ return true;
+ }
+
+ if (typeof(StringName) == type)
+ {
+ res = VariantUtils.ConvertToStringNameObject(p_var);
+ return true;
+ }
+
+ if (typeof(NodePath) == type)
+ {
+ res = VariantUtils.ConvertToNodePathObject(p_var);
+ return true;
+ }
+
+ if (typeof(RID) == type)
+ {
+ res = VariantUtils.ConvertToRID(p_var);
+ return true;
+ }
+
+ if (typeof(Collections.Dictionary) == type)
+ {
+ res = VariantUtils.ConvertToDictionaryObject(p_var);
+ return true;
+ }
+
+ if (typeof(Collections.Array) == type)
+ {
+ res = VariantUtils.ConvertToArrayObject(p_var);
+ return true;
+ }
+
+ res = null;
+ return false;
+ }
+
+ public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var)
+ {
+ switch (p_var.Type)
+ {
+ case Variant.Type.Bool:
+ return p_var.Bool.ToBool();
+ case Variant.Type.Int:
+ return p_var.Int;
+ case Variant.Type.Float:
+ {
+#if REAL_T_IS_DOUBLE
+ return p_var.Float;
+#else
+ return (float)p_var.Float;
+#endif
+ }
+ case Variant.Type.String:
+ return ConvertStringToManaged(p_var.String);
+ case Variant.Type.Vector2:
+ return p_var.Vector2;
+ case Variant.Type.Vector2i:
+ return p_var.Vector2i;
+ case Variant.Type.Rect2:
+ return p_var.Rect2;
+ case Variant.Type.Rect2i:
+ return p_var.Rect2i;
+ case Variant.Type.Vector3:
+ return p_var.Vector3;
+ case Variant.Type.Vector3i:
+ return p_var.Vector3i;
+ case Variant.Type.Transform2d:
+ return *p_var.Transform2D;
+ case Variant.Type.Vector4:
+ return *p_var.Vector4;
+ case Variant.Type.Vector4i:
+ return *p_var.Vector4i;
+ case Variant.Type.Plane:
+ return p_var.Plane;
+ case Variant.Type.Quaternion:
+ return p_var.Quaternion;
+ case Variant.Type.Aabb:
+ return *p_var.AABB;
+ case Variant.Type.Basis:
+ return *p_var.Basis;
+ case Variant.Type.Transform3d:
+ return *p_var.Transform3D;
+ case Variant.Type.Projection:
+ return *p_var.Projection;
+ case Variant.Type.Color:
+ return p_var.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.StringName));
+ }
+ 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.NodePath));
+ }
+ case Variant.Type.Rid:
+ return p_var.RID;
+ case Variant.Type.Object:
+ return InteropUtils.UnmanagedGetManaged(p_var.Object);
+ case Variant.Type.Callable:
+ return ConvertCallableToManaged(p_var.Callable);
+ case Variant.Type.Signal:
+ return ConvertSignalToManaged(p_var.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.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.Array));
+ }
+ case Variant.Type.PackedByteArray:
+ return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
+ case Variant.Type.PackedInt32Array:
+ return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
+ case Variant.Type.PackedInt64Array:
+ return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
+ case Variant.Type.PackedFloat32Array:
+ return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
+ case Variant.Type.PackedFloat64Array:
+ return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
+ case Variant.Type.PackedStringArray:
+ return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
+ case Variant.Type.PackedVector2Array:
+ return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
+ case Variant.Type.PackedVector3Array:
+ return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
+ case Variant.Type.PackedColorArray:
+ return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
+ default:
+ return null;
+ }
+ }
+
+ // String
+
+ public static unsafe godot_string ConvertStringToNative(string? p_mono_string)
+ {
+ if (p_mono_string == null)
+ return new godot_string();
+
+ fixed (char* methodChars = p_mono_string)
+ {
+ NativeFuncs.godotsharp_string_new_with_utf16_chars(out godot_string dest, methodChars);
+ return dest;
+ }
+ }
+
+ public static unsafe string ConvertStringToManaged(in godot_string p_string)
+ {
+ if (p_string.Buffer == IntPtr.Zero)
+ return string.Empty;
+
+ const int sizeOfChar32 = 4;
+ byte* bytes = (byte*)p_string.Buffer;
+ int size = p_string.Size;
+ 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(in Callable p_managed_callable)
+ {
+ if (p_managed_callable.Delegate != null)
+ {
+ var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
+ NativeFuncs.godotsharp_callable_new_with_delegate(
+ GCHandle.ToIntPtr(gcHandle), out godot_callable callable);
+ return callable;
+ }
+ else
+ {
+ godot_string_name method;
+
+ if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty)
+ {
+ var src = (godot_string_name)p_managed_callable.Method.NativeValue;
+ method = NativeFuncs.godotsharp_string_name_new_copy(src);
+ }
+ else
+ {
+ method = default;
+ }
+
+ return new godot_callable(method /* Takes ownership of disposable */,
+ p_managed_callable.Target.GetInstanceId());
+ }
+ }
+
+ public static Callable ConvertCallableToManaged(in godot_callable p_callable)
+ {
+ if (NativeFuncs.godotsharp_callable_get_data_for_marshalling(p_callable,
+ out IntPtr delegateGCHandle, out IntPtr godotObject,
+ out godot_string_name name).ToBool())
+ {
+ 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(in SignalInfo p_managed_signal)
+ {
+ ulong ownerId = p_managed_signal.Owner.GetInstanceId();
+ godot_string_name name;
+
+ if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty)
+ {
+ var src = (godot_string_name)p_managed_signal.Name.NativeValue;
+ name = NativeFuncs.godotsharp_string_name_new_copy(src);
+ }
+ else
+ {
+ name = default;
+ }
+
+ return new godot_signal(name, ownerId);
+ }
+
+ public static SignalInfo ConvertSignalToManaged(in 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
+
+ internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(in godot_array p_array)
+ where T : Godot.Object
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new T[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = (T)array[i].AsGodotObject();
+
+ return ret;
+ }
+
+ // TODO: This needs reflection. Look for an alternative.
+ internal static Godot.Object[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(in godot_array p_array,
+ Type type)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = (Godot.Object[])Activator.CreateInstance(type, length)!;
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsGodotObject();
+
+ return ret;
+ }
+
+ internal static StringName[] ConvertNativeGodotArrayToSystemArrayOfStringName(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new StringName[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsStringName();
+
+ return ret;
+ }
+
+ internal static NodePath[] ConvertNativeGodotArrayToSystemArrayOfNodePath(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new NodePath[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsNodePath();
+
+ return ret;
+ }
+
+ internal static RID[] ConvertNativeGodotArrayToSystemArrayOfRID(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new RID[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsRID();
+
+ return ret;
+ }
+
+ // PackedByteArray
+
+ public static unsafe byte[] ConvertNativePackedByteArrayToSystemArray(in godot_packed_byte_array p_array)
+ {
+ byte* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<byte>();
+ var array = new byte[size];
+ fixed (byte* dest = array)
+ Buffer.MemoryCopy(buffer, dest, size, size);
+ return array;
+ }
+
+ public static unsafe godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(Span<byte> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_byte_array();
+ fixed (byte* src = p_array)
+ return NativeFuncs.godotsharp_packed_byte_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedInt32Array
+
+ public static unsafe int[] ConvertNativePackedInt32ArrayToSystemArray(godot_packed_int32_array p_array)
+ {
+ int* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<int>();
+ 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 ConvertSystemArrayToNativePackedInt32Array(Span<int> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_int32_array();
+ fixed (int* src = p_array)
+ return NativeFuncs.godotsharp_packed_int32_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedInt64Array
+
+ public static unsafe long[] ConvertNativePackedInt64ArrayToSystemArray(godot_packed_int64_array p_array)
+ {
+ long* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<long>();
+ 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 ConvertSystemArrayToNativePackedInt64Array(Span<long> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_int64_array();
+ fixed (long* src = p_array)
+ return NativeFuncs.godotsharp_packed_int64_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedFloat32Array
+
+ public static unsafe float[] ConvertNativePackedFloat32ArrayToSystemArray(godot_packed_float32_array p_array)
+ {
+ float* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<float>();
+ 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 ConvertSystemArrayToNativePackedFloat32Array(
+ Span<float> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_float32_array();
+ fixed (float* src = p_array)
+ return NativeFuncs.godotsharp_packed_float32_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedFloat64Array
+
+ public static unsafe double[] ConvertNativePackedFloat64ArrayToSystemArray(godot_packed_float64_array p_array)
+ {
+ double* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<double>();
+ 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 ConvertSystemArrayToNativePackedFloat64Array(
+ Span<double> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_float64_array();
+ fixed (double* src = p_array)
+ return NativeFuncs.godotsharp_packed_float64_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedStringArray
+
+ public static unsafe string[] ConvertNativePackedStringArrayToSystemArray(godot_packed_string_array p_array)
+ {
+ godot_string* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<string>();
+ var array = new string[size];
+ for (int i = 0; i < size; i++)
+ array[i] = ConvertStringToManaged(buffer[i]);
+ return array;
+ }
+
+ public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(Span<string> p_array)
+ {
+ godot_packed_string_array dest = new godot_packed_string_array();
+
+ if (p_array.IsEmpty)
+ return dest;
+
+ /* TODO: Replace godotsharp_packed_string_array_add with a single internal call to
+ get the write address. We can't use `dest._ptr` directly for writing due to COW. */
+
+ for (int i = 0; i < p_array.Length; i++)
+ {
+ using godot_string godotStrElem = ConvertStringToNative(p_array[i]);
+ NativeFuncs.godotsharp_packed_string_array_add(ref dest, godotStrElem);
+ }
+
+ return dest;
+ }
+
+ // PackedVector2Array
+
+ public static unsafe Vector2[] ConvertNativePackedVector2ArrayToSystemArray(godot_packed_vector2_array p_array)
+ {
+ Vector2* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Vector2>();
+ 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 ConvertSystemArrayToNativePackedVector2Array(
+ Span<Vector2> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_vector2_array();
+ fixed (Vector2* src = p_array)
+ return NativeFuncs.godotsharp_packed_vector2_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedVector3Array
+
+ public static unsafe Vector3[] ConvertNativePackedVector3ArrayToSystemArray(godot_packed_vector3_array p_array)
+ {
+ Vector3* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Vector3>();
+ 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 ConvertSystemArrayToNativePackedVector3Array(
+ Span<Vector3> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_vector3_array();
+ fixed (Vector3* src = p_array)
+ return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedColorArray
+
+ public static unsafe Color[] ConvertNativePackedColorArrayToSystemArray(godot_packed_color_array p_array)
+ {
+ Color* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Color>();
+ 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 ConvertSystemArrayToNativePackedColorArray(Span<Color> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_color_array();
+ fixed (Color* src = p_array)
+ return NativeFuncs.godotsharp_packed_color_array_new_mem_copy(src, p_array.Length);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
new file mode 100644
index 0000000000..6d2534e6f7
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -0,0 +1,522 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.SourceGenerators.Internal;
+
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ /*
+ * IMPORTANT:
+ * The order of the methods defined in NativeFuncs must match the order
+ * in the array defined at the bottom of 'glue/runtime_interop.cpp'.
+ */
+
+ [GenerateUnmanagedCallbacks(typeof(UnmanagedCallbacks))]
+ public static unsafe partial class NativeFuncs
+ {
+ private static bool initialized = false;
+
+ // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
+ public static void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ if (initialized)
+ throw new InvalidOperationException("Already initialized");
+ initialized = true;
+
+ if (unmanagedCallbacksSize != sizeof(UnmanagedCallbacks))
+ throw new ArgumentException("Unmanaged callbacks size mismatch");
+
+ _unmanagedCallbacks = Unsafe.AsRef<UnmanagedCallbacks>((void*)unmanagedCallbacks);
+ }
+
+ private partial struct UnmanagedCallbacks
+ {
+ }
+
+ // Custom functions
+
+ public static partial IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname,
+ in godot_string_name p_methodname);
+
+ public static partial delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(
+ in godot_string_name p_classname);
+
+ public static partial IntPtr godotsharp_engine_get_singleton(in godot_string p_name);
+
+
+ internal static partial Error godotsharp_stack_info_vector_resize(
+ ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector, int p_size);
+
+ internal static partial void godotsharp_stack_info_vector_destroy(
+ ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
+
+ internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
+ in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
+ godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
+
+ internal static partial bool godotsharp_internal_script_debugger_is_active();
+
+ internal static partial IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr);
+
+ internal static partial void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree);
+
+ internal static partial void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree,
+ godot_bool isFinalizer);
+
+ internal static partial Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
+ in godot_string_name signal,
+ IntPtr target, IntPtr awaiterHandlePtr);
+
+ internal static partial void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
+ IntPtr unmanaged, in godot_string_name nativeName, godot_bool refCounted);
+
+ internal static partial void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
+ IntPtr unmanaged, godot_ref* scriptPtr, godot_bool refCounted);
+
+ internal static partial void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
+ IntPtr gcHandleIntPtr, IntPtr unmanaged);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
+ out godot_bool r_has_cs_script_instance);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
+ IntPtr oldGCHandlePtr);
+
+ internal static partial void godotsharp_internal_new_csharp_script(godot_ref* r_dest);
+
+ internal static partial godot_bool godotsharp_internal_script_load(in godot_string p_path, godot_ref* r_dest);
+
+ internal static partial void godotsharp_internal_reload_registered_script(IntPtr scriptPtr);
+
+ internal static partial void godotsharp_array_filter_godot_objects_by_native(in godot_string_name p_native_name,
+ in godot_array p_input, out godot_array r_output);
+
+ internal static partial void godotsharp_array_filter_godot_objects_by_non_native(in godot_array p_input,
+ out godot_array r_output);
+
+ public static partial void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest,
+ IntPtr p_ref_counted_ptr);
+
+ public static partial void godotsharp_ref_destroy(ref godot_ref p_instance);
+
+ public static partial void godotsharp_string_name_new_from_string(out godot_string_name r_dest,
+ in godot_string p_name);
+
+ public static partial void godotsharp_node_path_new_from_string(out godot_node_path r_dest,
+ in godot_string p_name);
+
+ public static partial void
+ godotsharp_string_name_as_string(out godot_string r_dest, in godot_string_name p_name);
+
+ public static partial void godotsharp_node_path_as_string(out godot_string r_dest, in godot_node_path p_np);
+
+ public static partial godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src,
+ int p_length);
+
+ public static partial godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src,
+ int p_length);
+
+ public static partial godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src,
+ int p_length);
+
+ public static partial godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src,
+ int p_length);
+
+ public static partial godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src,
+ int p_length);
+
+ public static partial godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src,
+ int p_length);
+
+ public static partial godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src,
+ int p_length);
+
+ public static partial godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src,
+ int p_length);
+
+ public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
+ in godot_string p_element);
+
+ public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle,
+ out godot_callable r_callable);
+
+ internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable,
+ out IntPtr r_delegate_handle, out IntPtr r_object, out godot_string_name r_name);
+
+ internal static partial godot_variant godotsharp_callable_call(in godot_callable p_callable,
+ godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
+
+ internal static partial void godotsharp_callable_call_deferred(in godot_callable p_callable,
+ godot_variant** p_args, int p_arg_count);
+
+ // GDNative functions
+
+ // gdnative.h
+
+ public static partial void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args,
+ void* p_ret);
+
+ public static partial godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance,
+ godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
+
+ // variant.h
+
+ public static partial void
+ godotsharp_variant_new_string_name(out godot_variant r_dest, in godot_string_name p_s);
+
+ public static partial void godotsharp_variant_new_copy(out godot_variant r_dest, in godot_variant p_src);
+
+ public static partial void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np);
+
+ public static partial void godotsharp_variant_new_object(out godot_variant r_dest, IntPtr p_obj);
+
+ public static partial void godotsharp_variant_new_transform2d(out godot_variant r_dest, in Transform2D p_t2d);
+
+ public static partial void godotsharp_variant_new_vector4(out godot_variant r_dest, in Vector4 p_vec4);
+
+ public static partial void godotsharp_variant_new_vector4i(out godot_variant r_dest, in Vector4i p_vec4i);
+
+ public static partial void godotsharp_variant_new_basis(out godot_variant r_dest, in Basis p_basis);
+
+ public static partial void godotsharp_variant_new_transform3d(out godot_variant r_dest, in Transform3D p_trans);
+
+ public static partial void godotsharp_variant_new_projection(out godot_variant r_dest, in Projection p_proj);
+
+ public static partial void godotsharp_variant_new_aabb(out godot_variant r_dest, in AABB p_aabb);
+
+ public static partial void godotsharp_variant_new_dictionary(out godot_variant r_dest,
+ in godot_dictionary p_dict);
+
+ public static partial void godotsharp_variant_new_array(out godot_variant r_dest, in godot_array p_arr);
+
+ public static partial void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest,
+ in godot_packed_byte_array p_pba);
+
+ public static partial void godotsharp_variant_new_packed_int32_array(out godot_variant r_dest,
+ in godot_packed_int32_array p_pia);
+
+ public static partial void godotsharp_variant_new_packed_int64_array(out godot_variant r_dest,
+ in godot_packed_int64_array p_pia);
+
+ public static partial void godotsharp_variant_new_packed_float32_array(out godot_variant r_dest,
+ in godot_packed_float32_array p_pra);
+
+ public static partial void godotsharp_variant_new_packed_float64_array(out godot_variant r_dest,
+ in godot_packed_float64_array p_pra);
+
+ public static partial void godotsharp_variant_new_packed_string_array(out godot_variant r_dest,
+ in godot_packed_string_array p_psa);
+
+ public static partial void godotsharp_variant_new_packed_vector2_array(out godot_variant r_dest,
+ in godot_packed_vector2_array p_pv2a);
+
+ public static partial void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest,
+ in godot_packed_vector3_array p_pv3a);
+
+ public static partial void godotsharp_variant_new_packed_color_array(out godot_variant r_dest,
+ in godot_packed_color_array p_pca);
+
+ public static partial godot_bool godotsharp_variant_as_bool(in godot_variant p_self);
+
+ public static partial Int64 godotsharp_variant_as_int(in godot_variant p_self);
+
+ public static partial double godotsharp_variant_as_float(in godot_variant p_self);
+
+ public static partial godot_string godotsharp_variant_as_string(in godot_variant p_self);
+
+ public static partial Vector2 godotsharp_variant_as_vector2(in godot_variant p_self);
+
+ public static partial Vector2i godotsharp_variant_as_vector2i(in godot_variant p_self);
+
+ public static partial Rect2 godotsharp_variant_as_rect2(in godot_variant p_self);
+
+ public static partial Rect2i godotsharp_variant_as_rect2i(in godot_variant p_self);
+
+ public static partial Vector3 godotsharp_variant_as_vector3(in godot_variant p_self);
+
+ public static partial Vector3i godotsharp_variant_as_vector3i(in godot_variant p_self);
+
+ public static partial Transform2D godotsharp_variant_as_transform2d(in godot_variant p_self);
+
+ public static partial Vector4 godotsharp_variant_as_vector4(in godot_variant p_self);
+
+ public static partial Vector4i godotsharp_variant_as_vector4i(in godot_variant p_self);
+
+ public static partial Plane godotsharp_variant_as_plane(in godot_variant p_self);
+
+ public static partial Quaternion godotsharp_variant_as_quaternion(in godot_variant p_self);
+
+ public static partial AABB godotsharp_variant_as_aabb(in godot_variant p_self);
+
+ public static partial Basis godotsharp_variant_as_basis(in godot_variant p_self);
+
+ public static partial Transform3D godotsharp_variant_as_transform3d(in godot_variant p_self);
+
+ public static partial Projection godotsharp_variant_as_projection(in godot_variant p_self);
+
+ public static partial Color godotsharp_variant_as_color(in godot_variant p_self);
+
+ public static partial godot_string_name godotsharp_variant_as_string_name(in godot_variant p_self);
+
+ public static partial godot_node_path godotsharp_variant_as_node_path(in godot_variant p_self);
+
+ public static partial RID godotsharp_variant_as_rid(in godot_variant p_self);
+
+ public static partial godot_callable godotsharp_variant_as_callable(in godot_variant p_self);
+
+ public static partial godot_signal godotsharp_variant_as_signal(in godot_variant p_self);
+
+ public static partial godot_dictionary godotsharp_variant_as_dictionary(in godot_variant p_self);
+
+ public static partial godot_array godotsharp_variant_as_array(in godot_variant p_self);
+
+ public static partial godot_packed_byte_array godotsharp_variant_as_packed_byte_array(in godot_variant p_self);
+
+ public static partial godot_packed_int32_array godotsharp_variant_as_packed_int32_array(in godot_variant p_self);
+
+ public static partial godot_packed_int64_array godotsharp_variant_as_packed_int64_array(in godot_variant p_self);
+
+ public static partial godot_packed_float32_array godotsharp_variant_as_packed_float32_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_float64_array godotsharp_variant_as_packed_float64_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_string_array godotsharp_variant_as_packed_string_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self);
+
+ public static partial godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b);
+
+ // string.h
+
+ public static partial void godotsharp_string_new_with_utf16_chars(out godot_string r_dest, char* p_contents);
+
+ // string_name.h
+
+ public static partial void godotsharp_string_name_new_copy(out godot_string_name r_dest,
+ in godot_string_name p_src);
+
+ // node_path.h
+
+ public static partial void godotsharp_node_path_new_copy(out godot_node_path r_dest, in godot_node_path p_src);
+
+ // array.h
+
+ public static partial void godotsharp_array_new(out godot_array r_dest);
+
+ public static partial void godotsharp_array_new_copy(out godot_array r_dest, in godot_array p_src);
+
+ public static partial godot_variant* godotsharp_array_ptrw(ref godot_array p_self);
+
+ // dictionary.h
+
+ public static partial void godotsharp_dictionary_new(out godot_dictionary r_dest);
+
+ public static partial void godotsharp_dictionary_new_copy(out godot_dictionary r_dest,
+ in godot_dictionary p_src);
+
+ // destroy functions
+
+ public static partial void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self);
+
+ public static partial void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self);
+
+ public static partial void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self);
+
+ public static partial void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self);
+
+ public static partial void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self);
+
+ public static partial void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self);
+
+ public static partial void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self);
+
+ public static partial void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self);
+
+ public static partial void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self);
+
+ public static partial void godotsharp_variant_destroy(ref godot_variant p_self);
+
+ public static partial void godotsharp_string_destroy(ref godot_string p_self);
+
+ public static partial void godotsharp_string_name_destroy(ref godot_string_name p_self);
+
+ public static partial void godotsharp_node_path_destroy(ref godot_node_path p_self);
+
+ public static partial void godotsharp_signal_destroy(ref godot_signal p_self);
+
+ public static partial void godotsharp_callable_destroy(ref godot_callable p_self);
+
+ public static partial void godotsharp_array_destroy(ref godot_array p_self);
+
+ public static partial void godotsharp_dictionary_destroy(ref godot_dictionary p_self);
+
+ // Array
+
+ public static partial int godotsharp_array_add(ref godot_array p_self, in godot_variant p_item);
+
+ public static partial void
+ godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest);
+
+ public static partial int godotsharp_array_index_of(ref godot_array p_self, in godot_variant p_item);
+
+ public static partial void godotsharp_array_insert(ref godot_array p_self, int p_index, in godot_variant p_item);
+
+ public static partial void godotsharp_array_remove_at(ref godot_array p_self, int p_index);
+
+ public static partial Error godotsharp_array_resize(ref godot_array p_self, int p_new_size);
+
+ public static partial Error godotsharp_array_shuffle(ref godot_array p_self);
+
+ public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str);
+
+ // Dictionary
+
+ public static partial godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self,
+ in godot_variant p_key,
+ out godot_variant r_value);
+
+ public static partial void godotsharp_dictionary_set_value(ref godot_dictionary p_self, in godot_variant p_key,
+ in godot_variant p_value);
+
+ public static partial void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest);
+
+ public static partial void godotsharp_dictionary_values(ref godot_dictionary p_self, out godot_array r_dest);
+
+ public static partial int godotsharp_dictionary_count(ref godot_dictionary p_self);
+
+ public static partial 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);
+
+ public static partial void godotsharp_dictionary_add(ref godot_dictionary p_self, in godot_variant p_key,
+ in godot_variant p_value);
+
+ public static partial void godotsharp_dictionary_clear(ref godot_dictionary p_self);
+
+ public static partial godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self,
+ in godot_variant p_key);
+
+ public static partial void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep,
+ out godot_dictionary r_dest);
+
+ public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self,
+ in godot_variant p_key);
+
+ public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str);
+
+ // StringExtensions
+
+ public static partial void godotsharp_string_md5_buffer(in godot_string p_self,
+ out godot_packed_byte_array r_md5_buffer);
+
+ public static partial void godotsharp_string_md5_text(in godot_string p_self, out godot_string r_md5_text);
+
+ public static partial int godotsharp_string_rfind(in godot_string p_self, in godot_string p_what, int p_from);
+
+ public static partial int godotsharp_string_rfindn(in godot_string p_self, in godot_string p_what, int p_from);
+
+ public static partial void godotsharp_string_sha256_buffer(in godot_string p_self,
+ out godot_packed_byte_array r_sha256_buffer);
+
+ public static partial void godotsharp_string_sha256_text(in godot_string p_self,
+ out godot_string r_sha256_text);
+
+ public static partial void godotsharp_string_simplify_path(in godot_string p_self,
+ out godot_string r_simplified_path);
+
+ // NodePath
+
+ public static partial void godotsharp_node_path_get_as_property_path(in godot_node_path p_self,
+ ref godot_node_path r_dest);
+
+ public static partial void godotsharp_node_path_get_concatenated_names(in godot_node_path p_self,
+ out godot_string r_names);
+
+ public static partial void godotsharp_node_path_get_concatenated_subnames(in godot_node_path p_self,
+ out godot_string r_subnames);
+
+ public static partial void godotsharp_node_path_get_name(in godot_node_path p_self, int p_idx,
+ out godot_string r_name);
+
+ public static partial int godotsharp_node_path_get_name_count(in godot_node_path p_self);
+
+ public static partial void godotsharp_node_path_get_subname(in godot_node_path p_self, int p_idx,
+ out godot_string r_subname);
+
+ public static partial int godotsharp_node_path_get_subname_count(in godot_node_path p_self);
+
+ public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self);
+
+ // GD, etc
+
+ internal static partial void godotsharp_bytes2var(in godot_packed_byte_array p_bytes,
+ godot_bool p_allow_objects,
+ out godot_variant r_ret);
+
+ internal static partial void godotsharp_convert(in godot_variant p_what, int p_type,
+ out godot_variant r_ret);
+
+ internal static partial int godotsharp_hash(in godot_variant p_var);
+
+ internal static partial IntPtr godotsharp_instance_from_id(ulong p_instance_id);
+
+ internal static partial void godotsharp_print(in godot_string p_what);
+
+ public static partial void godotsharp_print_rich(in godot_string p_what);
+
+ internal static partial void godotsharp_printerr(in godot_string p_what);
+
+ internal static partial void godotsharp_printraw(in godot_string p_what);
+
+ internal static partial void godotsharp_prints(in godot_string p_what);
+
+ internal static partial void godotsharp_printt(in godot_string p_what);
+
+ internal static partial float godotsharp_randf();
+
+ internal static partial uint godotsharp_randi();
+
+ internal static partial void godotsharp_randomize();
+
+ internal static partial double godotsharp_randf_range(double from, double to);
+
+ internal static partial double godotsharp_randfn(double mean, double deviation);
+
+ internal static partial int godotsharp_randi_range(int from, int to);
+
+ internal static partial uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed);
+
+ internal static partial void godotsharp_seed(ulong seed);
+
+ internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref);
+
+ internal static partial void godotsharp_str(in godot_array p_what, out godot_string r_ret);
+
+ internal static partial void godotsharp_str2var(in godot_string p_str, out godot_variant r_ret);
+
+ internal static partial void godotsharp_var2bytes(in godot_variant p_what, godot_bool p_full_objects,
+ out godot_packed_byte_array r_bytes);
+
+ internal static partial void godotsharp_var2str(in godot_variant p_var, out godot_string r_ret);
+
+ internal static partial void godotsharp_pusherror(in godot_string p_str);
+
+ internal static partial void godotsharp_pushwarning(in godot_string p_str);
+
+ // Object
+
+ public static partial void godotsharp_object_to_string(IntPtr ptr, out godot_string r_str);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs
new file mode 100644
index 0000000000..2ea3c18d26
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs
@@ -0,0 +1,99 @@
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ public static partial class NativeFuncs
+ {
+ public static godot_variant godotsharp_variant_new_copy(in godot_variant src)
+ {
+ switch (src.Type)
+ {
+ case Variant.Type.Nil:
+ return default;
+ case Variant.Type.Bool:
+ return new godot_variant() { Bool = src.Bool };
+ case Variant.Type.Int:
+ return new godot_variant() { Int = src.Int };
+ case Variant.Type.Float:
+ return new godot_variant() { Float = src.Float };
+ case Variant.Type.Vector2:
+ return new godot_variant() { Vector2 = src.Vector2 };
+ case Variant.Type.Vector2i:
+ return new godot_variant() { Vector2i = src.Vector2i };
+ case Variant.Type.Rect2:
+ return new godot_variant() { Rect2 = src.Rect2 };
+ case Variant.Type.Rect2i:
+ return new godot_variant() { Rect2i = src.Rect2i };
+ case Variant.Type.Vector3:
+ return new godot_variant() { Vector3 = src.Vector3 };
+ case Variant.Type.Vector3i:
+ return new godot_variant() { Vector3i = src.Vector3i };
+ case Variant.Type.Plane:
+ return new godot_variant() { Plane = src.Plane };
+ case Variant.Type.Quaternion:
+ return new godot_variant() { Quaternion = src.Quaternion };
+ case Variant.Type.Color:
+ return new godot_variant() { Color = src.Color };
+ case Variant.Type.Rid:
+ return new godot_variant() { RID = src.RID };
+ }
+
+ godotsharp_variant_new_copy(out godot_variant ret, src);
+ return ret;
+ }
+
+ public static godot_string_name godotsharp_string_name_new_copy(in godot_string_name src)
+ {
+ if (src.IsEmpty)
+ return default;
+ godotsharp_string_name_new_copy(out godot_string_name ret, src);
+ return ret;
+ }
+
+ public static godot_node_path godotsharp_node_path_new_copy(in godot_node_path src)
+ {
+ if (src.IsEmpty)
+ return default;
+ godotsharp_node_path_new_copy(out godot_node_path ret, src);
+ return ret;
+ }
+
+ public static godot_array godotsharp_array_new()
+ {
+ godotsharp_array_new(out godot_array ret);
+ return ret;
+ }
+
+ public static godot_array godotsharp_array_new_copy(in godot_array src)
+ {
+ godotsharp_array_new_copy(out godot_array ret, src);
+ return ret;
+ }
+
+ public static godot_dictionary godotsharp_dictionary_new()
+ {
+ godotsharp_dictionary_new(out godot_dictionary ret);
+ return ret;
+ }
+
+ public static godot_dictionary godotsharp_dictionary_new_copy(in godot_dictionary src)
+ {
+ godotsharp_dictionary_new_copy(out godot_dictionary ret, src);
+ return ret;
+ }
+
+ public static godot_string_name godotsharp_string_name_new_from_string(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ godotsharp_string_name_new_from_string(out godot_string_name ret, src);
+ return ret;
+ }
+
+ public static godot_node_path godotsharp_node_path_new_from_string(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ godotsharp_node_path_new_from_string(out godot_node_path ret, src);
+ return ret;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
new file mode 100644
index 0000000000..422df74c23
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
@@ -0,0 +1,20 @@
+using System.Runtime.CompilerServices;
+
+namespace Godot.NativeInterop
+{
+ // Our source generators will add trampolines methods that access variant arguments.
+ // This struct makes that possible without having to enable `AllowUnsafeBlocks` in game projects.
+
+ public unsafe ref struct NativeVariantPtrArgs
+ {
+ private godot_variant** _args;
+
+ internal NativeVariantPtrArgs(godot_variant** args) => _args = args;
+
+ public ref godot_variant this[int index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref *_args[index];
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs
new file mode 100644
index 0000000000..2b5bf2e142
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs
@@ -0,0 +1,976 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.NativeInterop;
+
+internal static unsafe class VariantConversionCallbacks
+{
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ internal static delegate* <in T, godot_variant> GetToVariantCallback<T>()
+ {
+ static godot_variant FromBool(in bool @bool) =>
+ VariantUtils.CreateFromBool(@bool);
+
+ static godot_variant FromChar(in char @char) =>
+ VariantUtils.CreateFromInt(@char);
+
+ static godot_variant FromInt8(in sbyte @int8) =>
+ VariantUtils.CreateFromInt(@int8);
+
+ static godot_variant FromInt16(in short @int16) =>
+ VariantUtils.CreateFromInt(@int16);
+
+ static godot_variant FromInt32(in int @int32) =>
+ VariantUtils.CreateFromInt(@int32);
+
+ static godot_variant FromInt64(in long @int64) =>
+ VariantUtils.CreateFromInt(@int64);
+
+ static godot_variant FromUInt8(in byte @uint8) =>
+ VariantUtils.CreateFromInt(@uint8);
+
+ static godot_variant FromUInt16(in ushort @uint16) =>
+ VariantUtils.CreateFromInt(@uint16);
+
+ static godot_variant FromUInt32(in uint @uint32) =>
+ VariantUtils.CreateFromInt(@uint32);
+
+ static godot_variant FromUInt64(in ulong @uint64) =>
+ VariantUtils.CreateFromInt(@uint64);
+
+ static godot_variant FromFloat(in float @float) =>
+ VariantUtils.CreateFromFloat(@float);
+
+ static godot_variant FromDouble(in double @double) =>
+ VariantUtils.CreateFromFloat(@double);
+
+ static godot_variant FromVector2(in Vector2 @vector2) =>
+ VariantUtils.CreateFromVector2(@vector2);
+
+ static godot_variant FromVector2I(in Vector2i vector2I) =>
+ VariantUtils.CreateFromVector2i(vector2I);
+
+ static godot_variant FromRect2(in Rect2 @rect2) =>
+ VariantUtils.CreateFromRect2(@rect2);
+
+ static godot_variant FromRect2I(in Rect2i rect2I) =>
+ VariantUtils.CreateFromRect2i(rect2I);
+
+ static godot_variant FromTransform2D(in Transform2D @transform2D) =>
+ VariantUtils.CreateFromTransform2D(@transform2D);
+
+ static godot_variant FromVector3(in Vector3 @vector3) =>
+ VariantUtils.CreateFromVector3(@vector3);
+
+ static godot_variant FromVector3I(in Vector3i vector3I) =>
+ VariantUtils.CreateFromVector3i(vector3I);
+
+ static godot_variant FromBasis(in Basis @basis) =>
+ VariantUtils.CreateFromBasis(@basis);
+
+ static godot_variant FromQuaternion(in Quaternion @quaternion) =>
+ VariantUtils.CreateFromQuaternion(@quaternion);
+
+ static godot_variant FromTransform3D(in Transform3D @transform3d) =>
+ VariantUtils.CreateFromTransform3D(@transform3d);
+
+ static godot_variant FromAabb(in AABB @aabb) =>
+ VariantUtils.CreateFromAABB(@aabb);
+
+ static godot_variant FromColor(in Color @color) =>
+ VariantUtils.CreateFromColor(@color);
+
+ static godot_variant FromPlane(in Plane @plane) =>
+ VariantUtils.CreateFromPlane(@plane);
+
+ static godot_variant FromCallable(in Callable @callable) =>
+ VariantUtils.CreateFromCallable(@callable);
+
+ static godot_variant FromSignalInfo(in SignalInfo @signalInfo) =>
+ VariantUtils.CreateFromSignalInfo(@signalInfo);
+
+ static godot_variant FromString(in string @string) =>
+ VariantUtils.CreateFromString(@string);
+
+ static godot_variant FromByteArray(in byte[] byteArray) =>
+ VariantUtils.CreateFromPackedByteArray(byteArray);
+
+ static godot_variant FromInt32Array(in int[] int32Array) =>
+ VariantUtils.CreateFromPackedInt32Array(int32Array);
+
+ static godot_variant FromInt64Array(in long[] int64Array) =>
+ VariantUtils.CreateFromPackedInt64Array(int64Array);
+
+ static godot_variant FromFloatArray(in float[] floatArray) =>
+ VariantUtils.CreateFromPackedFloat32Array(floatArray);
+
+ static godot_variant FromDoubleArray(in double[] doubleArray) =>
+ VariantUtils.CreateFromPackedFloat64Array(doubleArray);
+
+ static godot_variant FromStringArray(in string[] stringArray) =>
+ VariantUtils.CreateFromPackedStringArray(stringArray);
+
+ static godot_variant FromVector2Array(in Vector2[] vector2Array) =>
+ VariantUtils.CreateFromPackedVector2Array(vector2Array);
+
+ static godot_variant FromVector3Array(in Vector3[] vector3Array) =>
+ VariantUtils.CreateFromPackedVector3Array(vector3Array);
+
+ static godot_variant FromColorArray(in Color[] colorArray) =>
+ VariantUtils.CreateFromPackedColorArray(colorArray);
+
+ static godot_variant FromStringNameArray(in StringName[] stringNameArray) =>
+ VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
+
+ static godot_variant FromNodePathArray(in NodePath[] nodePathArray) =>
+ VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
+
+ static godot_variant FromRidArray(in RID[] ridArray) =>
+ VariantUtils.CreateFromSystemArrayOfRID(ridArray);
+
+ static godot_variant FromGodotObject(in Godot.Object godotObject) =>
+ VariantUtils.CreateFromGodotObject(godotObject);
+
+ static godot_variant FromStringName(in StringName stringName) =>
+ VariantUtils.CreateFromStringName(stringName);
+
+ static godot_variant FromNodePath(in NodePath nodePath) =>
+ VariantUtils.CreateFromNodePath(nodePath);
+
+ static godot_variant FromRid(in RID rid) =>
+ VariantUtils.CreateFromRID(rid);
+
+ static godot_variant FromGodotDictionary(in Collections.Dictionary godotDictionary) =>
+ VariantUtils.CreateFromDictionary(godotDictionary);
+
+ static godot_variant FromGodotArray(in Collections.Array godotArray) =>
+ VariantUtils.CreateFromArray(godotArray);
+
+ static godot_variant FromVariant(in Variant variant) =>
+ NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
+
+ var typeOfT = typeof(T);
+
+ if (typeOfT == typeof(bool))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in bool, godot_variant>)
+ &FromBool;
+ }
+
+ if (typeOfT == typeof(char))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in char, godot_variant>)
+ &FromChar;
+ }
+
+ if (typeOfT == typeof(sbyte))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in sbyte, godot_variant>)
+ &FromInt8;
+ }
+
+ if (typeOfT == typeof(short))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in short, godot_variant>)
+ &FromInt16;
+ }
+
+ if (typeOfT == typeof(int))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in int, godot_variant>)
+ &FromInt32;
+ }
+
+ if (typeOfT == typeof(long))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in long, godot_variant>)
+ &FromInt64;
+ }
+
+ if (typeOfT == typeof(byte))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in byte, godot_variant>)
+ &FromUInt8;
+ }
+
+ if (typeOfT == typeof(ushort))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ushort, godot_variant>)
+ &FromUInt16;
+ }
+
+ if (typeOfT == typeof(uint))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in uint, godot_variant>)
+ &FromUInt32;
+ }
+
+ if (typeOfT == typeof(ulong))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ulong, godot_variant>)
+ &FromUInt64;
+ }
+
+ if (typeOfT == typeof(float))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in float, godot_variant>)
+ &FromFloat;
+ }
+
+ if (typeOfT == typeof(double))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in double, godot_variant>)
+ &FromDouble;
+ }
+
+ if (typeOfT == typeof(Vector2))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector2, godot_variant>)
+ &FromVector2;
+ }
+
+ if (typeOfT == typeof(Vector2i))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector2i, godot_variant>)
+ &FromVector2I;
+ }
+
+ if (typeOfT == typeof(Rect2))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Rect2, godot_variant>)
+ &FromRect2;
+ }
+
+ if (typeOfT == typeof(Rect2i))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Rect2i, godot_variant>)
+ &FromRect2I;
+ }
+
+ if (typeOfT == typeof(Transform2D))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Transform2D, godot_variant>)
+ &FromTransform2D;
+ }
+
+ if (typeOfT == typeof(Vector3))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector3, godot_variant>)
+ &FromVector3;
+ }
+
+ if (typeOfT == typeof(Vector3i))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector3i, godot_variant>)
+ &FromVector3I;
+ }
+
+ if (typeOfT == typeof(Basis))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Basis, godot_variant>)
+ &FromBasis;
+ }
+
+ if (typeOfT == typeof(Quaternion))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Quaternion, godot_variant>)
+ &FromQuaternion;
+ }
+
+ if (typeOfT == typeof(Transform3D))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Transform3D, godot_variant>)
+ &FromTransform3D;
+ }
+
+ if (typeOfT == typeof(AABB))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in AABB, godot_variant>)
+ &FromAabb;
+ }
+
+ if (typeOfT == typeof(Color))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Color, godot_variant>)
+ &FromColor;
+ }
+
+ if (typeOfT == typeof(Plane))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Plane, godot_variant>)
+ &FromPlane;
+ }
+
+ if (typeOfT == typeof(Callable))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Callable, godot_variant>)
+ &FromCallable;
+ }
+
+ if (typeOfT == typeof(SignalInfo))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in SignalInfo, godot_variant>)
+ &FromSignalInfo;
+ }
+
+ if (typeOfT.IsEnum)
+ {
+ var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
+
+ switch (Type.GetTypeCode(enumUnderlyingType))
+ {
+ case TypeCode.SByte:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in sbyte, godot_variant>)
+ &FromInt8;
+ }
+ case TypeCode.Int16:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in short, godot_variant>)
+ &FromInt16;
+ }
+ case TypeCode.Int32:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in int, godot_variant>)
+ &FromInt32;
+ }
+ case TypeCode.Int64:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in long, godot_variant>)
+ &FromInt64;
+ }
+ case TypeCode.Byte:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in byte, godot_variant>)
+ &FromUInt8;
+ }
+ case TypeCode.UInt16:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ushort, godot_variant>)
+ &FromUInt16;
+ }
+ case TypeCode.UInt32:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in uint, godot_variant>)
+ &FromUInt32;
+ }
+ case TypeCode.UInt64:
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in ulong, godot_variant>)
+ &FromUInt64;
+ }
+ default:
+ return null;
+ }
+ }
+
+ if (typeOfT == typeof(string))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in string, godot_variant>)
+ &FromString;
+ }
+
+ if (typeOfT == typeof(byte[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in byte[], godot_variant>)
+ &FromByteArray;
+ }
+
+ if (typeOfT == typeof(int[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in int[], godot_variant>)
+ &FromInt32Array;
+ }
+
+ if (typeOfT == typeof(long[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in long[], godot_variant>)
+ &FromInt64Array;
+ }
+
+ if (typeOfT == typeof(float[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in float[], godot_variant>)
+ &FromFloatArray;
+ }
+
+ if (typeOfT == typeof(double[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in double[], godot_variant>)
+ &FromDoubleArray;
+ }
+
+ if (typeOfT == typeof(string[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in string[], godot_variant>)
+ &FromStringArray;
+ }
+
+ if (typeOfT == typeof(Vector2[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector2[], godot_variant>)
+ &FromVector2Array;
+ }
+
+ if (typeOfT == typeof(Vector3[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Vector3[], godot_variant>)
+ &FromVector3Array;
+ }
+
+ if (typeOfT == typeof(Color[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Color[], godot_variant>)
+ &FromColorArray;
+ }
+
+ if (typeOfT == typeof(StringName[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in StringName[], godot_variant>)
+ &FromStringNameArray;
+ }
+
+ if (typeOfT == typeof(NodePath[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in NodePath[], godot_variant>)
+ &FromNodePathArray;
+ }
+
+ if (typeOfT == typeof(RID[]))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in RID[], godot_variant>)
+ &FromRidArray;
+ }
+
+ if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Godot.Object, godot_variant>)
+ &FromGodotObject;
+ }
+
+ if (typeOfT == typeof(StringName))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in StringName, godot_variant>)
+ &FromStringName;
+ }
+
+ if (typeOfT == typeof(NodePath))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in NodePath, godot_variant>)
+ &FromNodePath;
+ }
+
+ if (typeOfT == typeof(RID))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in RID, godot_variant>)
+ &FromRid;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Dictionary))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Godot.Collections.Dictionary, godot_variant>)
+ &FromGodotDictionary;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Array))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Godot.Collections.Array, godot_variant>)
+ &FromGodotArray;
+ }
+
+ if (typeOfT == typeof(Variant))
+ {
+ return (delegate* <in T, godot_variant>)(delegate* <in Variant, godot_variant>)
+ &FromVariant;
+ }
+
+ return null;
+ }
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ internal static delegate* <in godot_variant, T> GetToManagedCallback<T>()
+ {
+ static bool ToBool(in godot_variant variant) =>
+ VariantUtils.ConvertToBool(variant);
+
+ static char ToChar(in godot_variant variant) =>
+ VariantUtils.ConvertToChar(variant);
+
+ static sbyte ToInt8(in godot_variant variant) =>
+ VariantUtils.ConvertToInt8(variant);
+
+ static short ToInt16(in godot_variant variant) =>
+ VariantUtils.ConvertToInt16(variant);
+
+ static int ToInt32(in godot_variant variant) =>
+ VariantUtils.ConvertToInt32(variant);
+
+ static long ToInt64(in godot_variant variant) =>
+ VariantUtils.ConvertToInt64(variant);
+
+ static byte ToUInt8(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt8(variant);
+
+ static ushort ToUInt16(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt16(variant);
+
+ static uint ToUInt32(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt32(variant);
+
+ static ulong ToUInt64(in godot_variant variant) =>
+ VariantUtils.ConvertToUInt64(variant);
+
+ static float ToFloat(in godot_variant variant) =>
+ VariantUtils.ConvertToFloat32(variant);
+
+ static double ToDouble(in godot_variant variant) =>
+ VariantUtils.ConvertToFloat64(variant);
+
+ static Vector2 ToVector2(in godot_variant variant) =>
+ VariantUtils.ConvertToVector2(variant);
+
+ static Vector2i ToVector2I(in godot_variant variant) =>
+ VariantUtils.ConvertToVector2i(variant);
+
+ static Rect2 ToRect2(in godot_variant variant) =>
+ VariantUtils.ConvertToRect2(variant);
+
+ static Rect2i ToRect2I(in godot_variant variant) =>
+ VariantUtils.ConvertToRect2i(variant);
+
+ static Transform2D ToTransform2D(in godot_variant variant) =>
+ VariantUtils.ConvertToTransform2D(variant);
+
+ static Vector3 ToVector3(in godot_variant variant) =>
+ VariantUtils.ConvertToVector3(variant);
+
+ static Vector3i ToVector3I(in godot_variant variant) =>
+ VariantUtils.ConvertToVector3i(variant);
+
+ static Basis ToBasis(in godot_variant variant) =>
+ VariantUtils.ConvertToBasis(variant);
+
+ static Quaternion ToQuaternion(in godot_variant variant) =>
+ VariantUtils.ConvertToQuaternion(variant);
+
+ static Transform3D ToTransform3D(in godot_variant variant) =>
+ VariantUtils.ConvertToTransform3D(variant);
+
+ static AABB ToAabb(in godot_variant variant) =>
+ VariantUtils.ConvertToAABB(variant);
+
+ static Color ToColor(in godot_variant variant) =>
+ VariantUtils.ConvertToColor(variant);
+
+ static Plane ToPlane(in godot_variant variant) =>
+ VariantUtils.ConvertToPlane(variant);
+
+ static Callable ToCallable(in godot_variant variant) =>
+ VariantUtils.ConvertToCallableManaged(variant);
+
+ static SignalInfo ToSignalInfo(in godot_variant variant) =>
+ VariantUtils.ConvertToSignalInfo(variant);
+
+ static string ToString(in godot_variant variant) =>
+ VariantUtils.ConvertToStringObject(variant);
+
+ static byte[] ToByteArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedByteArrayToSystemArray(variant);
+
+ static int[] ToInt32Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(variant);
+
+ static long[] ToInt64Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(variant);
+
+ static float[] ToFloatArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(variant);
+
+ static double[] ToDoubleArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(variant);
+
+ static string[] ToStringArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedStringArrayToSystemArray(variant);
+
+ static Vector2[] ToVector2Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(variant);
+
+ static Vector3[] ToVector3Array(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(variant);
+
+ static Color[] ToColorArray(in godot_variant variant) =>
+ VariantUtils.ConvertAsPackedColorArrayToSystemArray(variant);
+
+ static StringName[] ToStringNameArray(in godot_variant variant) =>
+ VariantUtils.ConvertToSystemArrayOfStringName(variant);
+
+ static NodePath[] ToNodePathArray(in godot_variant variant) =>
+ VariantUtils.ConvertToSystemArrayOfNodePath(variant);
+
+ static RID[] ToRidArray(in godot_variant variant) =>
+ VariantUtils.ConvertToSystemArrayOfRID(variant);
+
+ static Godot.Object ToGodotObject(in godot_variant variant) =>
+ VariantUtils.ConvertToGodotObject(variant);
+
+ static StringName ToStringName(in godot_variant variant) =>
+ VariantUtils.ConvertToStringNameObject(variant);
+
+ static NodePath ToNodePath(in godot_variant variant) =>
+ VariantUtils.ConvertToNodePathObject(variant);
+
+ static RID ToRid(in godot_variant variant) =>
+ VariantUtils.ConvertToRID(variant);
+
+ static Collections.Dictionary ToGodotDictionary(in godot_variant variant) =>
+ VariantUtils.ConvertToDictionaryObject(variant);
+
+ static Collections.Array ToGodotArray(in godot_variant variant) =>
+ VariantUtils.ConvertToArrayObject(variant);
+
+ static Variant ToVariant(in godot_variant variant) =>
+ Variant.CreateCopyingBorrowed(variant);
+
+ var typeOfT = typeof(T);
+
+ // ReSharper disable RedundantCast
+ // Rider is being stupid here. These casts are definitely needed. We get build errors without them.
+
+ if (typeOfT == typeof(bool))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, bool>)
+ &ToBool;
+ }
+
+ if (typeOfT == typeof(char))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, char>)
+ &ToChar;
+ }
+
+ if (typeOfT == typeof(sbyte))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, sbyte>)
+ &ToInt8;
+ }
+
+ if (typeOfT == typeof(short))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, short>)
+ &ToInt16;
+ }
+
+ if (typeOfT == typeof(int))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, int>)
+ &ToInt32;
+ }
+
+ if (typeOfT == typeof(long))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, long>)
+ &ToInt64;
+ }
+
+ if (typeOfT == typeof(byte))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, byte>)
+ &ToUInt8;
+ }
+
+ if (typeOfT == typeof(ushort))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ushort>)
+ &ToUInt16;
+ }
+
+ if (typeOfT == typeof(uint))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, uint>)
+ &ToUInt32;
+ }
+
+ if (typeOfT == typeof(ulong))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ulong>)
+ &ToUInt64;
+ }
+
+ if (typeOfT == typeof(float))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, float>)
+ &ToFloat;
+ }
+
+ if (typeOfT == typeof(double))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, double>)
+ &ToDouble;
+ }
+
+ if (typeOfT == typeof(Vector2))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector2>)
+ &ToVector2;
+ }
+
+ if (typeOfT == typeof(Vector2i))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector2i>)
+ &ToVector2I;
+ }
+
+ if (typeOfT == typeof(Rect2))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Rect2>)
+ &ToRect2;
+ }
+
+ if (typeOfT == typeof(Rect2i))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Rect2i>)
+ &ToRect2I;
+ }
+
+ if (typeOfT == typeof(Transform2D))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Transform2D>)
+ &ToTransform2D;
+ }
+
+ if (typeOfT == typeof(Vector3))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector3>)
+ &ToVector3;
+ }
+
+ if (typeOfT == typeof(Vector3i))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector3i>)
+ &ToVector3I;
+ }
+
+ if (typeOfT == typeof(Basis))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Basis>)
+ &ToBasis;
+ }
+
+ if (typeOfT == typeof(Quaternion))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Quaternion>)
+ &ToQuaternion;
+ }
+
+ if (typeOfT == typeof(Transform3D))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Transform3D>)
+ &ToTransform3D;
+ }
+
+ if (typeOfT == typeof(AABB))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, AABB>)
+ &ToAabb;
+ }
+
+ if (typeOfT == typeof(Color))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Color>)
+ &ToColor;
+ }
+
+ if (typeOfT == typeof(Plane))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Plane>)
+ &ToPlane;
+ }
+
+ if (typeOfT == typeof(Callable))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Callable>)
+ &ToCallable;
+ }
+
+ if (typeOfT == typeof(SignalInfo))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, SignalInfo>)
+ &ToSignalInfo;
+ }
+
+ if (typeOfT.IsEnum)
+ {
+ var enumUnderlyingType = typeOfT.GetEnumUnderlyingType();
+
+ switch (Type.GetTypeCode(enumUnderlyingType))
+ {
+ case TypeCode.SByte:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, sbyte>)
+ &ToInt8;
+ }
+ case TypeCode.Int16:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, short>)
+ &ToInt16;
+ }
+ case TypeCode.Int32:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, int>)
+ &ToInt32;
+ }
+ case TypeCode.Int64:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, long>)
+ &ToInt64;
+ }
+ case TypeCode.Byte:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, byte>)
+ &ToUInt8;
+ }
+ case TypeCode.UInt16:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ushort>)
+ &ToUInt16;
+ }
+ case TypeCode.UInt32:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, uint>)
+ &ToUInt32;
+ }
+ case TypeCode.UInt64:
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, ulong>)
+ &ToUInt64;
+ }
+ default:
+ return null;
+ }
+ }
+
+ if (typeOfT == typeof(string))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, string>)
+ &ToString;
+ }
+
+ if (typeOfT == typeof(byte[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, byte[]>)
+ &ToByteArray;
+ }
+
+ if (typeOfT == typeof(int[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, int[]>)
+ &ToInt32Array;
+ }
+
+ if (typeOfT == typeof(long[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, long[]>)
+ &ToInt64Array;
+ }
+
+ if (typeOfT == typeof(float[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, float[]>)
+ &ToFloatArray;
+ }
+
+ if (typeOfT == typeof(double[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, double[]>)
+ &ToDoubleArray;
+ }
+
+ if (typeOfT == typeof(string[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, string[]>)
+ &ToStringArray;
+ }
+
+ if (typeOfT == typeof(Vector2[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector2[]>)
+ &ToVector2Array;
+ }
+
+ if (typeOfT == typeof(Vector3[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Vector3[]>)
+ &ToVector3Array;
+ }
+
+ if (typeOfT == typeof(Color[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Color[]>)
+ &ToColorArray;
+ }
+
+ if (typeOfT == typeof(StringName[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, StringName[]>)
+ &ToStringNameArray;
+ }
+
+ if (typeOfT == typeof(NodePath[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, NodePath[]>)
+ &ToNodePathArray;
+ }
+
+ if (typeOfT == typeof(RID[]))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, RID[]>)
+ &ToRidArray;
+ }
+
+ if (typeof(Godot.Object).IsAssignableFrom(typeOfT))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Godot.Object>)
+ &ToGodotObject;
+ }
+
+ if (typeOfT == typeof(StringName))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, StringName>)
+ &ToStringName;
+ }
+
+ if (typeOfT == typeof(NodePath))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, NodePath>)
+ &ToNodePath;
+ }
+
+ if (typeOfT == typeof(RID))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, RID>)
+ &ToRid;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Dictionary))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Godot.Collections.Dictionary>)
+ &ToGodotDictionary;
+ }
+
+ if (typeOfT == typeof(Godot.Collections.Array))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Godot.Collections.Array>)
+ &ToGodotArray;
+ }
+
+ if (typeOfT == typeof(Variant))
+ {
+ return (delegate* <in godot_variant, T>)(delegate* <in godot_variant, Variant>)
+ &ToVariant;
+ }
+
+ // ReSharper restore RedundantCast
+
+ return null;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs
new file mode 100644
index 0000000000..46f31bbf4e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantSpanHelpers.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace Godot.NativeInterop
+{
+ internal readonly ref struct VariantSpanDisposer
+ {
+ private readonly Span<godot_variant.movable> _variantSpan;
+
+ // IMPORTANT: The span element must be default initialized.
+ // Make sure call Clear() on the span if it was created with stackalloc.
+ public VariantSpanDisposer(Span<godot_variant.movable> variantSpan)
+ {
+ _variantSpan = variantSpan;
+ }
+
+ public void Dispose()
+ {
+ for (int i = 0; i < _variantSpan.Length; i++)
+ _variantSpan[i].DangerousSelfRef.Dispose();
+ }
+ }
+
+ internal static class VariantSpanExtensions
+ {
+ // Used to make sure we always initialize the span values to the default,
+ // as we need that in order to safely dispose all elements after.
+ public static Span<godot_variant.movable> Cleared(this Span<godot_variant.movable> span)
+ {
+ span.Clear();
+ return span;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
new file mode 100644
index 0000000000..491ccf904e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
@@ -0,0 +1,611 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.Collections;
+
+// ReSharper disable InconsistentNaming
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public static class VariantUtils
+ {
+ public static godot_variant CreateFromRID(RID from)
+ => new() { Type = Variant.Type.Rid, RID = from };
+
+ public static godot_variant CreateFromBool(bool from)
+ => new() { Type = Variant.Type.Bool, Bool = from.ToGodotBool() };
+
+ public static godot_variant CreateFromInt(long from)
+ => new() { Type = Variant.Type.Int, Int = from };
+
+ public static godot_variant CreateFromInt(ulong from)
+ => new() { Type = Variant.Type.Int, Int = (long)from };
+
+ public static godot_variant CreateFromFloat(double from)
+ => new() { Type = Variant.Type.Float, Float = from };
+
+ public static godot_variant CreateFromVector2(Vector2 from)
+ => new() { Type = Variant.Type.Vector2, Vector2 = from };
+
+ public static godot_variant CreateFromVector2i(Vector2i from)
+ => new() { Type = Variant.Type.Vector2i, Vector2i = from };
+
+ public static godot_variant CreateFromVector3(Vector3 from)
+ => new() { Type = Variant.Type.Vector3, Vector3 = from };
+
+ public static godot_variant CreateFromVector3i(Vector3i from)
+ => new() { Type = Variant.Type.Vector3i, Vector3i = from };
+
+ public static godot_variant CreateFromRect2(Rect2 from)
+ => new() { Type = Variant.Type.Rect2, Rect2 = from };
+
+ public static godot_variant CreateFromRect2i(Rect2i from)
+ => new() { Type = Variant.Type.Rect2i, Rect2i = from };
+
+ public static godot_variant CreateFromQuaternion(Quaternion from)
+ => new() { Type = Variant.Type.Quaternion, Quaternion = from };
+
+ public static godot_variant CreateFromColor(Color from)
+ => new() { Type = Variant.Type.Color, Color = from };
+
+ public static godot_variant CreateFromPlane(Plane from)
+ => new() { Type = Variant.Type.Plane, Plane = from };
+
+ public static godot_variant CreateFromTransform2D(Transform2D from)
+ {
+ NativeFuncs.godotsharp_variant_new_transform2d(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromVector4(Vector4 from)
+ {
+ NativeFuncs.godotsharp_variant_new_vector4(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromVector4i(Vector4i from)
+ {
+ NativeFuncs.godotsharp_variant_new_vector4i(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromBasis(Basis from)
+ {
+ NativeFuncs.godotsharp_variant_new_basis(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromTransform3D(Transform3D from)
+ {
+ NativeFuncs.godotsharp_variant_new_transform3d(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromProjection(Projection from)
+ {
+ NativeFuncs.godotsharp_variant_new_projection(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromAABB(AABB from)
+ {
+ NativeFuncs.godotsharp_variant_new_aabb(out godot_variant ret, from);
+ return ret;
+ }
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from)
+ => new() { Type = Variant.Type.Callable, Callable = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromCallable(Callable from)
+ => CreateFromCallableTakingOwnershipOfDisposableValue(
+ Marshaling.ConvertCallableToNative(from));
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from)
+ => new() { Type = Variant.Type.Signal, Signal = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromSignalInfo(SignalInfo from)
+ => CreateFromSignalTakingOwnershipOfDisposableValue(
+ Marshaling.ConvertSignalToNative(from));
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
+ => new() { Type = Variant.Type.String, String = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromString(string? from)
+ => CreateFromStringTakingOwnershipOfDisposableValue(Marshaling.ConvertStringToNative(from));
+
+ public static godot_variant CreateFromPackedByteArray(in godot_packed_byte_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_byte_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedInt32Array(in godot_packed_int32_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_int32_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedInt64Array(in godot_packed_int64_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_int64_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedFloat32Array(in godot_packed_float32_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_float32_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedFloat64Array(in godot_packed_float64_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_float64_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedStringArray(in godot_packed_string_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_string_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedVector2Array(in godot_packed_vector2_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_vector2_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedVector3Array(in godot_packed_vector3_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_vector3_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedColorArray(in godot_packed_color_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_color_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedByteArray(Span<byte> from)
+ => CreateFromPackedByteArray(Marshaling.ConvertSystemArrayToNativePackedByteArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedInt32Array(Span<int> from)
+ => CreateFromPackedInt32Array(Marshaling.ConvertSystemArrayToNativePackedInt32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedInt64Array(Span<long> from)
+ => CreateFromPackedInt64Array(Marshaling.ConvertSystemArrayToNativePackedInt64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedFloat32Array(Span<float> from)
+ => CreateFromPackedFloat32Array(Marshaling.ConvertSystemArrayToNativePackedFloat32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedFloat64Array(Span<double> from)
+ => CreateFromPackedFloat64Array(Marshaling.ConvertSystemArrayToNativePackedFloat64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedStringArray(Span<string> from)
+ => CreateFromPackedStringArray(Marshaling.ConvertSystemArrayToNativePackedStringArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedVector2Array(Span<Vector2> from)
+ => CreateFromPackedVector2Array(Marshaling.ConvertSystemArrayToNativePackedVector2Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedVector3Array(Span<Vector3> from)
+ => CreateFromPackedVector3Array(Marshaling.ConvertSystemArrayToNativePackedVector3Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedColorArray(Span<Color> from)
+ => CreateFromPackedColorArray(Marshaling.ConvertSystemArrayToNativePackedColorArray(from));
+
+ public static godot_variant CreateFromSystemArrayOfStringName(Span<StringName> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ public static godot_variant CreateFromSystemArrayOfNodePath(Span<NodePath> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ public static godot_variant CreateFromSystemArrayOfRID(Span<RID> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ // ReSharper disable once RedundantNameQualifier
+ public static godot_variant CreateFromSystemArrayOfGodotObject(Godot.Object[]? from)
+ {
+ if (from == null)
+ return default; // Nil
+ using var fromGodot = new Collections.Array(from);
+ return CreateFromArray((godot_array)fromGodot.NativeValue);
+ }
+
+ public static godot_variant CreateFromArray(godot_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromArray(Collections.Array? from)
+ => from != null ? CreateFromArray((godot_array)from.NativeValue) : default;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromArray<T>(Array<T>? from)
+ => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default;
+
+ public static godot_variant CreateFromDictionary(godot_dictionary from)
+ {
+ NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromDictionary(Dictionary? from)
+ => from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from)
+ => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default;
+
+ public static godot_variant CreateFromStringName(godot_string_name from)
+ {
+ NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromStringName(StringName? from)
+ => from != null ? CreateFromStringName((godot_string_name)from.NativeValue) : default;
+
+ public static godot_variant CreateFromNodePath(godot_node_path from)
+ {
+ NativeFuncs.godotsharp_variant_new_node_path(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromNodePath(NodePath? from)
+ => from != null ? CreateFromNodePath((godot_node_path)from.NativeValue) : default;
+
+ public static godot_variant CreateFromGodotObjectPtr(IntPtr from)
+ {
+ if (from == IntPtr.Zero)
+ return new godot_variant();
+ NativeFuncs.godotsharp_variant_new_object(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // ReSharper disable once RedundantNameQualifier
+ public static godot_variant CreateFromGodotObject(Godot.Object? from)
+ => from != null ? CreateFromGodotObjectPtr(Object.GetPtr(from)) : default;
+
+ // We avoid the internal call if the stored type is the same we want.
+
+ public static bool ConvertToBool(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Bool ?
+ p_var.Bool.ToBool() :
+ NativeFuncs.godotsharp_variant_as_bool(p_var).ToBool();
+
+ public static char ConvertToChar(in godot_variant p_var)
+ => (char)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static sbyte ConvertToInt8(in godot_variant p_var)
+ => (sbyte)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static short ConvertToInt16(in godot_variant p_var)
+ => (short)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static int ConvertToInt32(in godot_variant p_var)
+ => (int)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static long ConvertToInt64(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var);
+
+ public static byte ConvertToUInt8(in godot_variant p_var)
+ => (byte)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static ushort ConvertToUInt16(in godot_variant p_var)
+ => (ushort)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static uint ConvertToUInt32(in godot_variant p_var)
+ => (uint)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static ulong ConvertToUInt64(in godot_variant p_var)
+ => (ulong)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static float ConvertToFloat32(in godot_variant p_var)
+ => (float)(p_var.Type == Variant.Type.Float ?
+ p_var.Float :
+ NativeFuncs.godotsharp_variant_as_float(p_var));
+
+ public static double ConvertToFloat64(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Float ?
+ p_var.Float :
+ NativeFuncs.godotsharp_variant_as_float(p_var);
+
+ public static Vector2 ConvertToVector2(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector2 ?
+ p_var.Vector2 :
+ NativeFuncs.godotsharp_variant_as_vector2(p_var);
+
+ public static Vector2i ConvertToVector2i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector2i ?
+ p_var.Vector2i :
+ NativeFuncs.godotsharp_variant_as_vector2i(p_var);
+
+ public static Rect2 ConvertToRect2(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rect2 ?
+ p_var.Rect2 :
+ NativeFuncs.godotsharp_variant_as_rect2(p_var);
+
+ public static Rect2i ConvertToRect2i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rect2i ?
+ p_var.Rect2i :
+ NativeFuncs.godotsharp_variant_as_rect2i(p_var);
+
+ public static unsafe Transform2D ConvertToTransform2D(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Transform2d ?
+ *p_var.Transform2D :
+ NativeFuncs.godotsharp_variant_as_transform2d(p_var);
+
+ public static Vector3 ConvertToVector3(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector3 ?
+ p_var.Vector3 :
+ NativeFuncs.godotsharp_variant_as_vector3(p_var);
+
+ public static Vector3i ConvertToVector3i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector3i ?
+ p_var.Vector3i :
+ NativeFuncs.godotsharp_variant_as_vector3i(p_var);
+
+ public static unsafe Vector4 ConvertToVector4(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector4 ?
+ *p_var.Vector4 :
+ NativeFuncs.godotsharp_variant_as_vector4(p_var);
+
+ public static unsafe Vector4i ConvertToVector4i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector4i ?
+ *p_var.Vector4i :
+ NativeFuncs.godotsharp_variant_as_vector4i(p_var);
+
+ public static unsafe Basis ConvertToBasis(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Basis ?
+ *p_var.Basis :
+ NativeFuncs.godotsharp_variant_as_basis(p_var);
+
+ public static Quaternion ConvertToQuaternion(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Quaternion ?
+ p_var.Quaternion :
+ NativeFuncs.godotsharp_variant_as_quaternion(p_var);
+
+ public static unsafe Transform3D ConvertToTransform3D(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Transform3d ?
+ *p_var.Transform3D :
+ NativeFuncs.godotsharp_variant_as_transform3d(p_var);
+
+ public static unsafe Projection ConvertToProjection(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Projection ?
+ *p_var.Projection :
+ NativeFuncs.godotsharp_variant_as_projection(p_var);
+
+ public static unsafe AABB ConvertToAABB(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Aabb ?
+ *p_var.AABB :
+ NativeFuncs.godotsharp_variant_as_aabb(p_var);
+
+ public static Color ConvertToColor(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Color ?
+ p_var.Color :
+ NativeFuncs.godotsharp_variant_as_color(p_var);
+
+ public static Plane ConvertToPlane(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Plane ?
+ p_var.Plane :
+ NativeFuncs.godotsharp_variant_as_plane(p_var);
+
+ public static RID ConvertToRID(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rid ?
+ p_var.RID :
+ NativeFuncs.godotsharp_variant_as_rid(p_var);
+
+ public static IntPtr ConvertToGodotObjectPtr(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // ReSharper disable once RedundantNameQualifier
+ public static Godot.Object ConvertToGodotObject(in godot_variant p_var)
+ => InteropUtils.UnmanagedGetManaged(ConvertToGodotObjectPtr(p_var));
+
+ public static string ConvertToStringObject(in godot_variant p_var)
+ {
+ switch (p_var.Type)
+ {
+ case Variant.Type.Nil:
+ return ""; // 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 Marshaling.ConvertStringToManaged(p_var.String);
+ }
+ default:
+ {
+ using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
+ return Marshaling.ConvertStringToManaged(godotString);
+ }
+ }
+ }
+
+ public static godot_string_name ConvertToStringName(in godot_variant p_var)
+ => p_var.Type == Variant.Type.StringName ?
+ NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName) :
+ NativeFuncs.godotsharp_variant_as_string_name(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static StringName ConvertToStringNameObject(in godot_variant p_var)
+ => StringName.CreateTakingOwnershipOfDisposableValue(ConvertToStringName(p_var));
+
+ public static godot_node_path ConvertToNodePath(in godot_variant p_var)
+ => p_var.Type == Variant.Type.NodePath ?
+ NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath) :
+ NativeFuncs.godotsharp_variant_as_node_path(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static NodePath ConvertToNodePathObject(in godot_variant p_var)
+ => NodePath.CreateTakingOwnershipOfDisposableValue(ConvertToNodePath(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_callable ConvertToCallable(in godot_variant p_var)
+ => NativeFuncs.godotsharp_variant_as_callable(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Callable ConvertToCallableManaged(in godot_variant p_var)
+ => Marshaling.ConvertCallableToManaged(ConvertToCallable(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_signal ConvertToSignal(in godot_variant p_var)
+ => NativeFuncs.godotsharp_variant_as_signal(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static SignalInfo ConvertToSignalInfo(in godot_variant p_var)
+ => Marshaling.ConvertSignalToManaged(ConvertToSignal(p_var));
+
+ public static godot_array ConvertToArray(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Array ?
+ NativeFuncs.godotsharp_array_new_copy(p_var.Array) :
+ NativeFuncs.godotsharp_variant_as_array(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Collections.Array ConvertToArrayObject(in godot_variant p_var)
+ => Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToArray(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Array<T> ConvertToArrayObject<T>(in godot_variant p_var)
+ => Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToArray(p_var));
+
+ public static godot_dictionary ConvertToDictionary(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Dictionary ?
+ NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary) :
+ NativeFuncs.godotsharp_variant_as_dictionary(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary ConvertToDictionaryObject(in godot_variant p_var)
+ => Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToDictionary(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary<TKey, TValue> ConvertToDictionaryObject<TKey, TValue>(in godot_variant p_var)
+ => Dictionary<TKey, TValue>.CreateTakingOwnershipOfDisposableValue(ConvertToDictionary(p_var));
+
+ public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var);
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(packedArray);
+ }
+
+ public static int[] ConvertAsPackedInt32ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var);
+ return Marshaling.ConvertNativePackedInt32ArrayToSystemArray(packedArray);
+ }
+
+ public static long[] ConvertAsPackedInt64ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var);
+ return Marshaling.ConvertNativePackedInt64ArrayToSystemArray(packedArray);
+ }
+
+ public static float[] ConvertAsPackedFloat32ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var);
+ return Marshaling.ConvertNativePackedFloat32ArrayToSystemArray(packedArray);
+ }
+
+ public static double[] ConvertAsPackedFloat64ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var);
+ return Marshaling.ConvertNativePackedFloat64ArrayToSystemArray(packedArray);
+ }
+
+ public static string[] ConvertAsPackedStringArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var);
+ return Marshaling.ConvertNativePackedStringArrayToSystemArray(packedArray);
+ }
+
+ public static Vector2[] ConvertAsPackedVector2ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var);
+ return Marshaling.ConvertNativePackedVector2ArrayToSystemArray(packedArray);
+ }
+
+ public static Vector3[] ConvertAsPackedVector3ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var);
+ return Marshaling.ConvertNativePackedVector3ArrayToSystemArray(packedArray);
+ }
+
+ public static Color[] ConvertAsPackedColorArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
+ return Marshaling.ConvertNativePackedColorArrayToSystemArray(packedArray);
+ }
+
+ public static StringName[] ConvertToSystemArrayOfStringName(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfStringName(godotArray);
+ }
+
+ public static NodePath[] ConvertToSystemArrayOfNodePath(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfNodePath(godotArray);
+ }
+
+ public static RID[] ConvertToSystemArrayOfRID(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRID(godotArray);
+ }
+
+ public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var)
+ // ReSharper disable once RedundantNameQualifier
+ where T : Godot.Object
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(godotArray);
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ public static Godot.Object[] ConvertToSystemArrayOfGodotObject(in godot_variant p_var, Type type)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(godotArray, type);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index 9ae01016cb..b02bd167a1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -39,22 +39,11 @@ namespace Godot
/// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene.
/// </code>
/// </example>
- public sealed partial class NodePath : IDisposable
+ public sealed class NodePath : IDisposable
{
- private bool _disposed = false;
+ internal godot_node_path.movable NativeValue;
- 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;
- }
+ private WeakReference<IDisposable> _weakReferenceToSelf;
~NodePath()
{
@@ -70,29 +59,33 @@ namespace Godot
GC.SuppressFinalize(this);
}
- private void Dispose(bool disposing)
+ public void Dispose(bool disposing)
{
- if (_disposed)
- return;
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
- if (ptr != IntPtr.Zero)
+ if (_weakReferenceToSelf != null)
{
- godot_icall_NodePath_Dtor(ptr);
- ptr = IntPtr.Zero;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
-
- _disposed = true;
}
- internal NodePath(IntPtr ptr)
+ private NodePath(godot_node_path nativeValueToOwn)
{
- this.ptr = ptr;
+ NativeValue = (godot_node_path.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
+ // Explicit name to make it very clear
+ internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn)
+ => new NodePath(nativeValueToOwn);
+
/// <summary>
/// Constructs an empty <see cref="NodePath"/>.
/// </summary>
- public NodePath() : this(string.Empty) { }
+ public NodePath()
+ {
+ }
/// <summary>
/// Constructs a <see cref="NodePath"/> from a string <paramref name="path"/>,
@@ -125,7 +118,11 @@ namespace Godot
/// <param name="path">A string that represents a path in a scene tree.</param>
public NodePath(string path)
{
- ptr = godot_icall_NodePath_Ctor(path);
+ if (!string.IsNullOrEmpty(path))
+ {
+ NativeValue = (godot_node_path.movable)NativeFuncs.godotsharp_node_path_new_from_string(path);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
@@ -138,7 +135,7 @@ namespace Godot
/// Converts this <see cref="NodePath"/> to a string.
/// </summary>
/// <param name="from">The <see cref="NodePath"/> to convert.</param>
- public static implicit operator string(NodePath from) => from.ToString();
+ public static implicit operator string(NodePath from) => from?.ToString();
/// <summary>
/// Converts this <see cref="NodePath"/> to a string.
@@ -146,7 +143,13 @@ namespace Godot
/// <returns>A string representation of this <see cref="NodePath"/>.</returns>
public override string ToString()
{
- return godot_icall_NodePath_operator_String(GetPtr(this));
+ if (IsEmpty)
+ return string.Empty;
+
+ var src = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_as_string(out godot_string dest, src);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
}
/// <summary>
@@ -166,7 +169,10 @@ namespace Godot
/// <returns>The <see cref="NodePath"/> as a pure property path.</returns>
public NodePath GetAsPropertyPath()
{
- return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this)));
+ godot_node_path propertyPath = default;
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_as_property_path(self, ref propertyPath);
+ return CreateTakingOwnershipOfDisposableValue(propertyPath);
}
/// <summary>
@@ -181,7 +187,10 @@ namespace Godot
/// <returns>The names concatenated with <c>/</c>.</returns>
public string GetConcatenatedNames()
{
- return godot_icall_NodePath_get_concatenated_names(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_concatenated_names(self, out godot_string names);
+ using (names)
+ return Marshaling.ConvertStringToManaged(names);
}
/// <summary>
@@ -195,9 +204,12 @@ namespace Godot
/// </code>
/// </example>
/// <returns>The subnames concatenated with <c>:</c>.</returns>
- public string GetConcatenatedSubnames()
+ public string GetConcatenatedSubNames()
{
- return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_concatenated_subnames(self, out godot_string subNames);
+ using (subNames)
+ return Marshaling.ConvertStringToManaged(subNames);
}
/// <summary>
@@ -215,28 +227,35 @@ namespace Godot
/// <returns>The name at the given index <paramref name="idx"/>.</returns>
public string GetName(int idx)
{
- return godot_icall_NodePath_get_name(GetPtr(this), idx);
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_name(self, idx, out godot_string name);
+ using (name)
+ return Marshaling.ConvertStringToManaged(name);
}
/// <summary>
/// Gets the number of node names which make up the path.
- /// Subnames (see <see cref="GetSubnameCount"/>) are not included.
+ /// Subnames (see <see cref="GetSubNameCount"/>) are not included.
/// For example, <c>"Path2D/PathFollow2D/Sprite2D"</c> has 3 names.
/// </summary>
/// <returns>The number of node names which make up the path.</returns>
public int GetNameCount()
{
- return godot_icall_NodePath_get_name_count(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_get_name_count(self);
}
/// <summary>
- /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubnameCount"/>).
+ /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubNameCount"/>).
/// </summary>
/// <param name="idx">The subname index.</param>
/// <returns>The subname at the given index <paramref name="idx"/>.</returns>
- public string GetSubname(int idx)
+ public string GetSubName(int idx)
{
- return godot_icall_NodePath_get_subname(GetPtr(this), idx);
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_subname(self, idx, out godot_string subName);
+ using (subName)
+ return Marshaling.ConvertStringToManaged(subName);
}
/// <summary>
@@ -245,9 +264,10 @@ namespace Godot
/// For example, <c>"Path2D/PathFollow2D/Sprite2D:texture:load_path"</c> has 2 subnames.
/// </summary>
/// <returns>The number of subnames in the path.</returns>
- public int GetSubnameCount()
+ public int GetSubNameCount()
{
- return godot_icall_NodePath_get_subname_count(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_get_subname_count(self);
}
/// <summary>
@@ -259,52 +279,14 @@ namespace Godot
/// <returns>If the <see cref="NodePath"/> is an absolute path.</returns>
public bool IsAbsolute()
{
- return godot_icall_NodePath_is_absolute(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_is_absolute(self).ToBool();
}
/// <summary>
/// Returns <see langword="true"/> if the node path is empty.
/// </summary>
/// <returns>If the <see cref="NodePath"/> is empty.</returns>
- public bool IsEmpty()
- {
- return godot_icall_NodePath_is_empty(GetPtr(this));
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_NodePath_Ctor(string path);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void godot_icall_NodePath_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_operator_String(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_concatenated_names(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
-
- [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);
+ public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index 746612477d..5cb678c280 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -1,54 +1,78 @@
using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
namespace Godot
{
public partial class Object : IDisposable
{
private bool _disposed = false;
+ private static readonly Type CachedType = typeof(Object);
- private static StringName nativeName = "Object";
+ internal IntPtr NativePtr;
+ private bool _memoryOwn;
- internal IntPtr ptr;
- internal bool memoryOwn;
+ private WeakReference<Object> _weakReferenceToSelf;
/// <summary>
/// Constructs a new <see cref="Object"/>.
/// </summary>
public Object() : this(false)
{
- if (ptr == IntPtr.Zero)
- ptr = godot_icall_Object_Ctor(this);
- _InitializeGodotScriptInstanceInternals();
+ unsafe
+ {
+ _ConstructAndInitialize(NativeCtor, NativeName, CachedType, refCounted: false);
+ }
}
- internal void _InitializeGodotScriptInstanceInternals()
+ internal unsafe void _ConstructAndInitialize(
+ delegate* unmanaged<IntPtr> nativeCtor,
+ StringName nativeName,
+ Type cachedType,
+ bool refCounted
+ )
{
- godot_icall_Object_ConnectEventSignals(ptr);
+ if (NativePtr == IntPtr.Zero)
+ {
+ NativePtr = nativeCtor();
+
+ InteropUtils.TieManagedToUnmanaged(this, NativePtr,
+ nativeName, refCounted, GetType(), cachedType);
+ }
+ else
+ {
+ InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr,
+ GetType(), cachedType);
+ }
+
+ _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
}
internal Object(bool memoryOwn)
{
- this.memoryOwn = memoryOwn;
+ _memoryOwn = memoryOwn;
}
/// <summary>
/// The pointer to the native instance of this <see cref="Object"/>.
/// </summary>
- public IntPtr NativeInstance
- {
- get { return ptr; }
- }
+ public IntPtr NativeInstance => NativePtr;
internal static IntPtr GetPtr(Object instance)
{
if (instance == null)
return IntPtr.Zero;
- if (instance._disposed)
+ // We check if NativePtr is null because this may be called by the debugger.
+ // If the debugger puts a breakpoint in one of the base constructors, before
+ // NativePtr is assigned, that would result in UB or crashes when calling
+ // native functions that receive the pointer, which can happen because the
+ // debugger calls ToString() and tries to get the value of properties.
+ if (instance._disposed || instance.NativePtr == IntPtr.Zero)
throw new ObjectDisposedException(instance.GetType().FullName);
- return instance.ptr;
+ return instance.NativePtr;
}
~Object()
@@ -73,22 +97,35 @@ namespace Godot
if (_disposed)
return;
- if (ptr != IntPtr.Zero)
+ _disposed = true;
+
+ if (NativePtr != IntPtr.Zero)
{
- if (memoryOwn)
+ IntPtr gcHandleToFree = NativeFuncs.godotsharp_internal_object_get_associated_gchandle(NativePtr);
+
+ if (gcHandleToFree != IntPtr.Zero)
+ {
+ object target = GCHandle.FromIntPtr(gcHandleToFree).Target;
+ // The GC handle may have been replaced in another thread. Release it only if
+ // it's associated to this managed instance, or if the target is no longer alive.
+ if (target != this && target != null)
+ gcHandleToFree = IntPtr.Zero;
+ }
+
+ if (_memoryOwn)
{
- memoryOwn = false;
- godot_icall_RefCounted_Disposed(this, ptr, !disposing);
+ NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, gcHandleToFree,
+ (!disposing).ToGodotBool());
}
else
{
- godot_icall_Object_Disposed(this, ptr);
+ NativeFuncs.godotsharp_internal_object_disposed(NativePtr, gcHandleToFree);
}
- ptr = IntPtr.Zero;
+ NativePtr = IntPtr.Zero;
}
- _disposed = true;
+ DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
}
/// <summary>
@@ -97,7 +134,9 @@ namespace Godot
/// <returns>A string representation of this object.</returns>
public override string ToString()
{
- return godot_icall_Object_ToString(GetPtr(this));
+ NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
/// <summary>
@@ -132,33 +171,72 @@ namespace Godot
return new SignalAwaiter(source, signal, this);
}
- /// <summary>
- /// Gets a new <see cref="DynamicGodotObject"/> associated with this instance.
- /// </summary>
- public dynamic DynamicObject => new DynamicGodotObject(this);
+ internal static Type InternalGetClassNativeBase(Type t)
+ {
+ do
+ {
+ var assemblyName = t.Assembly.GetName();
- internal static IntPtr __ClassDB_get_method(StringName type, string method)
+ if (assemblyName.Name == "GodotSharp")
+ return t;
+
+ if (assemblyName.Name == "GodotSharpEditor")
+ return t;
+ } while ((t = t.BaseType) != null);
+
+ return null;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
+ {
+ return false;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
+ {
+ value = default;
+ return false;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
+ NativeVariantPtrArgs args, int argCount)
{
- return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Object_Ctor(Object obj);
+ internal static IntPtr ClassDB_get_method(StringName type, StringName method)
+ {
+ var typeSelf = (godot_string_name)type.NativeValue;
+ var methodSelf = (godot_string_name)method.NativeValue;
+ IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
+
+ if (methodBind == IntPtr.Zero)
+ throw new NativeMethodBindNotFoundException(type + "." + method);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
+ return methodBind;
+ }
+
+ internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
+ {
+ // for some reason the '??' operator doesn't support 'delegate*'
+ var typeSelf = (godot_string_name)type.NativeValue;
+ var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
+ if (nativeConstructor == null)
+ throw new NativeConstructorNotFoundException(type);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
+ return nativeConstructor;
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Object_ToString(IntPtr ptr);
+ protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
+ {
+ }
- // Used by the generated API
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method);
+ // TODO: Should this be a constructor overload?
+ protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
+ {
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs
new file mode 100644
index 0000000000..eb2811c73d
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs
@@ -0,0 +1,135 @@
+using System;
+
+#nullable enable
+
+namespace Godot
+{
+ public partial class Object
+ {
+ public class NativeMemberNotFoundException : Exception
+ {
+ public NativeMemberNotFoundException()
+ {
+ }
+
+ public NativeMemberNotFoundException(string? message) : base(message)
+ {
+ }
+
+ public NativeMemberNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ public class NativeConstructorNotFoundException : NativeMemberNotFoundException
+ {
+ private readonly string? _nativeClassName;
+
+ // ReSharper disable once InconsistentNaming
+ private const string Arg_NativeConstructorNotFoundException = "Unable to find the native constructor.";
+
+ public NativeConstructorNotFoundException()
+ : base(Arg_NativeConstructorNotFoundException)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? nativeClassName)
+ : this(Arg_NativeConstructorNotFoundException, nativeClassName)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? message, string? nativeClassName)
+ : base(message)
+ {
+ _nativeClassName = nativeClassName;
+ }
+
+ public NativeConstructorNotFoundException(string? message, string? nativeClassName, Exception? innerException)
+ : base(message, innerException)
+ {
+ _nativeClassName = nativeClassName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ string s = base.Message;
+
+ if (string.IsNullOrEmpty(s))
+ {
+ s = Arg_NativeConstructorNotFoundException;
+ }
+
+ if (!string.IsNullOrEmpty(_nativeClassName))
+ {
+ s += " " + string.Format("(Class '{0}')", _nativeClassName);
+ }
+
+ return s;
+ }
+ }
+ }
+
+ public class NativeMethodBindNotFoundException : NativeMemberNotFoundException
+ {
+ private readonly string? _nativeMethodName;
+
+ // ReSharper disable once InconsistentNaming
+ private const string Arg_NativeMethodBindNotFoundException = "Unable to find the native method bind.";
+
+ public NativeMethodBindNotFoundException()
+ : base(Arg_NativeMethodBindNotFoundException)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? nativeMethodName)
+ : this(Arg_NativeMethodBindNotFoundException, nativeMethodName)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? message, string? nativeMethodName)
+ : base(message)
+ {
+ _nativeMethodName = nativeMethodName;
+ }
+
+ public NativeMethodBindNotFoundException(string? message, string? nativeMethodName, Exception? innerException)
+ : base(message, innerException)
+ {
+ _nativeMethodName = nativeMethodName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ string s = base.Message;
+
+ if (string.IsNullOrEmpty(s))
+ {
+ s = Arg_NativeMethodBindNotFoundException;
+ }
+
+ if (!string.IsNullOrEmpty(_nativeMethodName))
+ {
+ s += " " + string.Format("(Method '{0}')", _nativeMethodName);
+ }
+
+ return s;
+ }
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index fd97a71e47..50832d7679 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
index d774021131..d62cb6b0fa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -44,22 +39,22 @@ namespace Godot
}
/// <summary>
- /// The projections's X column. Also accessible by using the index position <c>[0]</c>.
+ /// The projection's X column. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public Vector4 x;
/// <summary>
- /// The projections's Y column. Also accessible by using the index position <c>[1]</c>.
+ /// The projection's Y column. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public Vector4 y;
/// <summary>
- /// The projections's Z column. Also accessible by using the index position <c>[2]</c>.
+ /// The projection's Z column. Also accessible by using the index position <c>[2]</c>.
/// </summary>
public Vector4 z;
/// <summary>
- /// The projections's W column. Also accessible by using the index position <c>[3]</c>.
+ /// The projection's W column. Also accessible by using the index position <c>[3]</c>.
/// </summary>
public Vector4 w;
@@ -79,18 +74,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Projection"/> from an existing <see cref="Projection"/>.
- /// </summary>
- /// <param name="proj">The existing <see cref="Projection"/>.</param>
- public Projection(Projection proj)
- {
- x = proj.x;
- y = proj.y;
- z = proj.z;
- w = proj.w;
- }
-
- /// <summary>
/// Constructs a new <see cref="Projection"/> from a <see cref="Transform3D"/>.
/// </summary>
/// <param name="transform">The <see cref="Transform3D"/>.</param>
@@ -360,7 +343,7 @@ namespace Godot
public int GetPixelsPerMeter(int forPixelWidth)
{
- Vector3 result = Xform(new Vector3(1, 0, -1));
+ Vector3 result = this * new Vector3(1, 0, -1);
return (int)((result.x * (real_t)0.5 + (real_t)0.5) * forPixelWidth);
}
@@ -593,19 +576,51 @@ namespace Godot
}
/// <summary>
- /// Returns a vector transformed (multiplied) by this projection.
+ /// Returns a Vector4 transformed (multiplied) by the projection.
+ /// </summary>
+ /// <param name="proj">The projection to apply.</param>
+ /// <param name="vector">A Vector4 to transform.</param>
+ /// <returns>The transformed Vector4.</returns>
+ public static Vector4 operator *(Projection proj, Vector4 vector)
+ {
+ return new Vector4(
+ proj.x.x * vector.x + proj.y.x * vector.y + proj.z.x * vector.z + proj.w.x * vector.w,
+ proj.x.y * vector.x + proj.y.y * vector.y + proj.z.y * vector.z + proj.w.y * vector.w,
+ proj.x.z * vector.x + proj.y.z * vector.y + proj.z.z * vector.z + proj.w.z * vector.w,
+ proj.x.w * vector.x + proj.y.w * vector.y + proj.z.w * vector.z + proj.w.w * vector.w
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector4 transformed (multiplied) by the inverse projection.
/// </summary>
/// <param name="proj">The projection to apply.</param>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public static Vector4 operator *(Projection proj, Vector4 v)
+ /// <param name="vector">A Vector4 to transform.</param>
+ /// <returns>The inversely transformed Vector4.</returns>
+ public static Vector4 operator *(Vector4 vector, Projection proj)
{
return new Vector4(
- proj.x.x * v.x + proj.y.x * v.y + proj.z.x * v.z + proj.w.x * v.w,
- proj.x.y * v.x + proj.y.y * v.y + proj.z.y * v.z + proj.w.y * v.w,
- proj.x.z * v.x + proj.y.z * v.y + proj.z.z * v.z + proj.w.z * v.w,
- proj.x.w * v.x + proj.y.w * v.y + proj.z.w * v.z + proj.w.w * v.w
+ proj.x.x * vector.x + proj.x.y * vector.y + proj.x.z * vector.z + proj.x.w * vector.w,
+ proj.y.x * vector.x + proj.y.y * vector.y + proj.y.z * vector.z + proj.y.w * vector.w,
+ proj.z.x * vector.x + proj.z.y * vector.y + proj.z.z * vector.z + proj.z.w * vector.w,
+ proj.w.x * vector.x + proj.w.y * vector.y + proj.w.z * vector.z + proj.w.w * vector.w
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the projection.
+ /// </summary>
+ /// <param name="proj">The projection to apply.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The transformed Vector3.</returns>
+ public static Vector3 operator *(Projection proj, Vector3 vector)
+ {
+ Vector3 ret = new Vector3(
+ proj.x.x * vector.x + proj.y.x * vector.y + proj.z.x * vector.z + proj.w.x,
+ proj.x.y * vector.x + proj.y.y * vector.y + proj.z.y * vector.z + proj.w.y,
+ proj.x.z * vector.x + proj.y.z * vector.y + proj.z.z * vector.z + proj.w.z
);
+ return ret / (proj.x.w * vector.x + proj.y.w * vector.y + proj.z.w * vector.z + proj.w.w);
}
/// <summary>
@@ -719,21 +734,6 @@ namespace Godot
}
}
- /// <summary>
- /// Returns a vector transformed (multiplied) by this projection.
- /// </summary>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- private Vector3 Xform(Vector3 v)
- {
- Vector3 ret = new Vector3(
- x.x * v.x + y.x * v.y + z.x * v.z + w.x,
- x.y * v.x + y.y * v.y + z.y * v.z + w.y,
- x.z * v.x + y.z * v.y + z.z * v.z + w.z
- );
- return ret / (x.w * v.x + y.w * v.y + z.w * v.z + w.w);
- }
-
// Constants
private static readonly Projection _zero = new Projection(
new Vector4(0, 0, 0, 0),
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index e38dca414f..658a14ca1d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -73,7 +68,7 @@ namespace Godot
case 3:
return w;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -93,7 +88,7 @@ namespace Godot
w = value;
break;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -318,24 +313,6 @@ namespace Godot
);
}
- /// <summary>
- /// Returns a vector transformed (multiplied) by this quaternion.
- /// </summary>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public Vector3 Xform(Vector3 v)
- {
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Quaternion is not normalized");
- }
-#endif
- var u = new Vector3(x, y, z);
- Vector3 uv = u.Cross(v);
- return v + (((uv * w) + u.Cross(uv)) * 2);
- }
-
// Constants
private static readonly Quaternion _identity = new Quaternion(0, 0, 0, 1);
@@ -363,15 +340,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a <see cref="Quaternion"/> from the given <see cref="Quaternion"/>.
- /// </summary>
- /// <param name="q">The existing quaternion.</param>
- public Quaternion(Quaternion q)
- {
- this = q;
- }
-
- /// <summary>
/// Constructs a <see cref="Quaternion"/> from the given <see cref="Basis"/>.
/// </summary>
/// <param name="basis">The <see cref="Basis"/> to construct from.</param>
@@ -466,6 +434,36 @@ namespace Godot
}
/// <summary>
+ /// Returns a Vector3 rotated (multiplied) by the quaternion.
+ /// </summary>
+ /// <param name="quaternion">The quaternion to rotate by.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The rotated Vector3.</returns>
+ public static Vector3 operator *(Quaternion quaternion, Vector3 vector)
+ {
+#if DEBUG
+ if (!quaternion.IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+#endif
+ var u = new Vector3(quaternion.x, quaternion.y, quaternion.z);
+ Vector3 uv = u.Cross(vector);
+ return vector + (((uv * quaternion.w) + u.Cross(uv)) * 2);
+ }
+
+ /// <summary>
+ /// Returns a Vector3 rotated (multiplied) by the inverse quaternion.
+ /// </summary>
+ /// <param name="vector">A Vector3 to inversely rotate.</param>
+ /// <param name="quaternion">The quaternion to rotate by.</param>
+ /// <returns>The inversely rotated Vector3.</returns>
+ public static Vector3 operator *(Vector3 vector, Quaternion quaternion)
+ {
+ return quaternion.Inverse() * vector;
+ }
+
+ /// <summary>
/// Adds each component of the left <see cref="Quaternion"/>
/// to the right <see cref="Quaternion"/>. This operation is not
/// meaningful on its own, but it can be used as a part of a
@@ -508,38 +506,6 @@ namespace Godot
}
/// <summary>
- /// Rotates (multiplies) the <see cref="Vector3"/>
- /// by the given <see cref="Quaternion"/>.
- /// </summary>
- /// <param name="quat">The quaternion to rotate by.</param>
- /// <param name="vec">The vector to rotate.</param>
- /// <returns>The rotated vector.</returns>
- public static Vector3 operator *(Quaternion quat, Vector3 vec)
- {
-#if DEBUG
- if (!quat.IsNormalized())
- {
- throw new InvalidOperationException("Quaternion is not normalized.");
- }
-#endif
- var u = new Vector3(quat.x, quat.y, quat.z);
- Vector3 uv = u.Cross(vec);
- return vec + (((uv * quat.w) + u.Cross(uv)) * 2);
- }
-
- /// <summary>
- /// Inversely rotates (multiplies) the <see cref="Vector3"/>
- /// by the given <see cref="Quaternion"/>.
- /// </summary>
- /// <param name="vec">The vector to rotate.</param>
- /// <param name="quat">The quaternion to rotate by.</param>
- /// <returns>The inversely rotated vector.</returns>
- public static Vector3 operator *(Vector3 vec, Quaternion quat)
- {
- return quat.Inverse() * vec;
- }
-
- /// <summary>
/// Multiplies each component of the <see cref="Quaternion"/>
/// by the given <see cref="real_t"/>. This operation is not
/// meaningful on its own, but it can be used as a part of a
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
index 1588869ec0..a31fef8360 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -9,99 +11,32 @@ namespace Godot
/// resource by themselves. They are used by and with the low-level Server
/// classes such as <see cref="RenderingServer"/>.
/// </summary>
- public sealed partial class RID : IDisposable
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RID
{
- private bool _disposed = false;
+ private ulong _id; // Default is 0
- internal IntPtr ptr;
-
- internal static IntPtr GetPtr(RID instance)
- {
- if (instance == null)
- throw new NullReferenceException($"The instance of type {nameof(RID)} is null.");
-
- if (instance._disposed)
- throw new ObjectDisposedException(instance.GetType().FullName);
-
- return instance.ptr;
- }
-
- ~RID()
- {
- Dispose(false);
- }
-
- /// <summary>
- /// Disposes of this <see cref="RID"/>.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
- {
- if (_disposed)
- return;
-
- if (ptr != IntPtr.Zero)
- {
- godot_icall_RID_Dtor(ptr);
- ptr = IntPtr.Zero;
- }
-
- _disposed = true;
- }
-
- internal RID(IntPtr ptr)
+ internal RID(ulong id)
{
- this.ptr = ptr;
- }
-
- /// <summary>
- /// The pointer to the native instance of this <see cref="RID"/>.
- /// </summary>
- public IntPtr NativeInstance
- {
- get { return ptr; }
- }
-
- internal RID()
- {
- this.ptr = IntPtr.Zero;
+ _id = id;
}
/// <summary>
/// Constructs a new <see cref="RID"/> for the given <see cref="Object"/> <paramref name="from"/>.
/// </summary>
public RID(Object from)
- {
- this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from));
- }
+ => _id = from is Resource res ? res.GetRid()._id : default;
/// <summary>
/// Returns the ID of the referenced resource.
/// </summary>
/// <returns>The ID of the referenced resource.</returns>
- public int GetId()
- {
- return godot_icall_RID_get_id(GetPtr(this));
- }
+ public ulong Id => _id;
/// <summary>
/// Converts this <see cref="RID"/> to a string.
/// </summary>
/// <returns>A string representation of this RID.</returns>
- public override string ToString() => "[RID]";
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_RID_Ctor(IntPtr from);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_RID_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_RID_get_id(IntPtr ptr);
+ public override string ToString() => $"RID({Id})";
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index ec16920fed..d2c9b0ca8b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
new file mode 100644
index 0000000000..ee605f8d8f
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Linq;
+
+#nullable enable
+
+namespace Godot;
+
+internal class ReflectionUtils
+{
+ public static Type? FindTypeInLoadedAssemblies(string assemblyName, string typeFullName)
+ {
+ return AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name == assemblyName)?
+ .GetType(typeFullName);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
index 2ba0493002..96fb891086 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
@@ -1,50 +1,67 @@
using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
- public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
+ public class SignalAwaiter : IAwaiter<Variant[]>, IAwaitable<Variant[]>
{
private bool _completed;
- private object[] _result;
- private Action _action;
+ private Variant[] _result;
+ private Action _continuation;
public SignalAwaiter(Object source, StringName signal, Object target)
{
- godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this);
+ var awaiterGcHandle = CustomGCHandle.AllocStrong(this);
+ using godot_string_name signalSrc = NativeFuncs.godotsharp_string_name_new_copy(
+ (godot_string_name)(signal?.NativeValue ?? default));
+ NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), in signalSrc,
+ Object.GetPtr(target), GCHandle.ToIntPtr(awaiterGcHandle));
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
+ public bool IsCompleted => _completed;
- public bool IsCompleted
+ public void OnCompleted(Action continuation)
{
- get
- {
- return _completed;
- }
+ _continuation = continuation;
}
- public void OnCompleted(Action action)
- {
- this._action = action;
- }
+ public Variant[] GetResult() => _result;
- public object[] GetResult()
- {
- return _result;
- }
+ public IAwaiter<Variant[]> GetAwaiter() => this;
- public IAwaiter<object[]> GetAwaiter()
+ [UnmanagedCallersOnly]
+ internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount,
+ godot_bool* outAwaiterIsNull)
{
- return this;
- }
+ try
+ {
+ var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target;
- internal void SignalCallback(object[] args)
- {
- _completed = true;
- _result = args;
- _action?.Invoke();
+ if (awaiter == null)
+ {
+ *outAwaiterIsNull = godot_bool.True;
+ return;
+ }
+
+ *outAwaiterIsNull = godot_bool.False;
+
+ awaiter._completed = true;
+
+ Variant[] signalArgs = new Variant[argCount];
+
+ for (int i = 0; i < argCount; i++)
+ signalArgs[i] = Variant.CreateCopyingBorrowed(*args[i]);
+
+ awaiter._result = signalArgs;
+
+ awaiter._continuation?.Invoke();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outAwaiterIsNull = godot_bool.False;
+ }
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
index da01300586..3f50df0a0d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
@@ -3,7 +3,7 @@ namespace Godot
/// <summary>
/// Represents a signal defined in an object.
/// </summary>
- public struct SignalInfo
+ public readonly struct SignalInfo
{
private readonly Object _owner;
private readonly StringName _signalName;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index a1f058ffe5..f0bc5949df 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Text.RegularExpressions;
+using Godot.NativeInterop;
+
+#nullable enable
namespace Godot
{
@@ -177,6 +179,7 @@ namespace Godot
{
return 0;
}
+
if (from == 0 && to == len)
{
str = instance;
@@ -214,7 +217,7 @@ namespace Godot
/// <returns>The escaped string.</returns>
public static string CEscape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\a", "\\a");
@@ -239,7 +242,7 @@ namespace Godot
/// <returns>The unescaped string.</returns>
public static string CUnescape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\a", "\a");
sb.Replace("\\b", "\b");
@@ -471,7 +474,8 @@ namespace Godot
/// <returns>The starting position of the substring, or -1 if not found.</returns>
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);
}
/// <summary>
@@ -490,7 +494,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);
}
/// <summary>Find the last occurrence of a substring.</summary>
@@ -519,7 +524,8 @@ namespace Godot
/// <returns>The starting position of the substring, or -1 if not found.</returns>
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);
}
/// <summary>
@@ -804,6 +810,7 @@ namespace Godot
{
match = instance[source] == text[target];
}
+
if (match)
{
source++;
@@ -926,7 +933,7 @@ namespace Godot
/// <returns>The escaped string.</returns>
public static string JSONEscape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\b", "\\b");
@@ -1015,15 +1022,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);
}
}
@@ -1070,12 +1080,12 @@ namespace Godot
/// <returns>The MD5 hash of the string.</returns>
public static byte[] MD5Buffer(this string instance)
{
- return godot_icall_String_md5_buffer(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_md5_buffer(instanceStr, out var md5Buffer);
+ using (md5Buffer)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(md5Buffer);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_String_md5_buffer(string str);
-
/// <summary>
/// Returns the MD5 hash of the string as a string.
/// </summary>
@@ -1084,12 +1094,12 @@ namespace Godot
/// <returns>The MD5 hash of the string.</returns>
public static string MD5Text(this string instance)
{
- return godot_icall_String_md5_text(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_md5_text(instanceStr, out var md5Text);
+ using (md5Text)
+ return Marshaling.ConvertStringToManaged(md5Text);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_md5_text(string str);
-
/// <summary>
/// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
@@ -1244,12 +1254,11 @@ namespace Godot
/// <returns>The position at which the substring was found, or -1 if not found.</returns>
public static int RFind(this string instance, string what, int from = -1)
{
- return godot_icall_String_rfind(instance, what, from);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ using godot_string whatStr = Marshaling.ConvertStringToNative(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);
-
/// <summary>
/// Perform a search for a substring, but start from the end of the string instead of the beginning.
/// Also search case-insensitive.
@@ -1261,12 +1270,11 @@ namespace Godot
/// <returns>The position at which the substring was found, or -1 if not found.</returns>
public static int RFindN(this string instance, string what, int from = -1)
{
- return godot_icall_String_rfindn(instance, what, from);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ using godot_string whatStr = Marshaling.ConvertStringToNative(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);
-
/// <summary>
/// Returns the right side of the string from a given position.
/// </summary>
@@ -1321,12 +1329,12 @@ namespace Godot
/// <returns>The SHA-256 hash of the string.</returns>
public static byte[] SHA256Buffer(this string instance)
{
- return godot_icall_String_sha256_buffer(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_sha256_buffer(instanceStr, out var sha256Buffer);
+ using (sha256Buffer)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(sha256Buffer);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_String_sha256_buffer(string str);
-
/// <summary>
/// Returns the SHA-256 hash of the string as a string.
/// </summary>
@@ -1335,12 +1343,12 @@ namespace Godot
/// <returns>The SHA-256 hash of the string.</returns>
public static string SHA256Text(this string instance)
{
- return godot_icall_String_sha256_text(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_sha256_text(instanceStr, out var sha256Text);
+ using (sha256Text)
+ return Marshaling.ConvertStringToManaged(sha256Text);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_sha256_text(string str);
-
/// <summary>
/// Returns the similarity index of the text compared to this string.
/// 1 means totally similar and 0 means totally dissimilar.
@@ -1355,6 +1363,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
@@ -1390,12 +1399,12 @@ namespace Godot
/// </summary>
public static string SimplifyPath(this string instance)
{
- return godot_icall_String_simplify_path(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_simplify_path(instanceStr, out godot_string simplifiedPath);
+ using (simplifiedPath)
+ return Marshaling.ConvertStringToManaged(simplifiedPath);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_simplify_path(string str);
-
/// <summary>
/// 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 +1418,8 @@ namespace Godot
/// <returns>The array of strings split from the string.</returns>
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);
}
/// <summary>
@@ -1605,9 +1615,9 @@ namespace Godot
/// <seealso cref="XMLEscape(string)"/>
/// <param name="instance">The string to unescape.</param>
/// <returns>The unescaped string.</returns>
- public static string XMLUnescape(this string instance)
+ public static string? XMLUnescape(this string instance)
{
- return SecurityElement.FromString(instance).Text;
+ return SecurityElement.FromString(instance)?.Text;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
index b1d504410b..b9ee0bc278 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -10,20 +10,11 @@ namespace Godot
/// Comparing them is much faster than with regular strings, because only the pointers are compared,
/// not the whole strings.
/// </summary>
- public sealed partial class StringName : IDisposable
+ public sealed class StringName : IDisposable
{
- private IntPtr ptr;
+ internal godot_string_name.movable NativeValue;
- 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;
- }
+ private WeakReference<IDisposable> _weakReferenceToSelf;
~StringName()
{
@@ -39,35 +30,45 @@ namespace Godot
GC.SuppressFinalize(this);
}
- private void Dispose(bool disposing)
+ public void Dispose(bool disposing)
{
- if (ptr != IntPtr.Zero)
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
{
- godot_icall_StringName_Dtor(ptr);
- ptr = IntPtr.Zero;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
}
- internal StringName(IntPtr ptr)
+ private StringName(godot_string_name nativeValueToOwn)
{
- this.ptr = ptr;
+ NativeValue = (godot_string_name.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
+ // Explicit name to make it very clear
+ internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn)
+ => new StringName(nativeValueToOwn);
+
/// <summary>
/// Constructs an empty <see cref="StringName"/>.
/// </summary>
public StringName()
{
- ptr = IntPtr.Zero;
}
/// <summary>
- /// Constructs a <see cref="StringName"/> from the given <paramref name="path"/> string.
+ /// Constructs a <see cref="StringName"/> from the given <paramref name="name"/> string.
/// </summary>
- /// <param name="path">String to construct the <see cref="StringName"/> from.</param>
- public StringName(string path)
+ /// <param name="name">String to construct the <see cref="StringName"/> from.</param>
+ public StringName(string name)
{
- ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
+ if (!string.IsNullOrEmpty(name))
+ {
+ NativeValue = (godot_string_name.movable)NativeFuncs.godotsharp_string_name_new_from_string(name);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
@@ -80,7 +81,7 @@ namespace Godot
/// Converts a <see cref="StringName"/> to a string.
/// </summary>
/// <param name="from">The <see cref="StringName"/> to convert.</param>
- public static implicit operator string(StringName from) => from.ToString();
+ public static implicit operator string(StringName from) => from?.ToString();
/// <summary>
/// Converts this <see cref="StringName"/> to a string.
@@ -88,28 +89,75 @@ namespace Godot
/// <returns>A string representation of this <see cref="StringName"/>.</returns>
public override string ToString()
{
- return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
+ if (IsEmpty)
+ return string.Empty;
+
+ var src = (godot_string_name)NativeValue;
+ NativeFuncs.godotsharp_string_name_as_string(out godot_string dest, src);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
}
/// <summary>
/// Check whether this <see cref="StringName"/> is empty.
/// </summary>
/// <returns>If the <see cref="StringName"/> is empty.</returns>
- public bool IsEmpty()
+ public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
+
+ public static bool operator ==(StringName left, StringName right)
{
- return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
+ if (left is null)
+ return right is null;
+ return left.Equals(right);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_StringName_Ctor(string path);
+ public static bool operator !=(StringName left, StringName right)
+ {
+ return !(left == right);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void godot_icall_StringName_Dtor(IntPtr ptr);
+ public bool Equals(StringName other)
+ {
+ if (other is null)
+ return false;
+ return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef;
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_StringName_operator_String(IntPtr ptr);
+ public static bool operator ==(StringName left, in godot_string_name right)
+ {
+ if (left is null)
+ return right.IsEmpty;
+ return left.Equals(right);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool godot_icall_StringName_is_empty(IntPtr ptr);
+ public static bool operator !=(StringName left, in godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public static bool operator ==(in godot_string_name left, StringName right)
+ {
+ return right == left;
+ }
+
+ public static bool operator !=(in godot_string_name left, StringName right)
+ {
+ return !(right == left);
+ }
+
+ public bool Equals(in godot_string_name other)
+ {
+ return NativeValue.DangerousSelfRef == other;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || (obj is StringName other && Equals(other));
+ }
+
+ public override int GetHashCode()
+ {
+ return NativeValue.GetHashCode();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 68d097eb4e..70cf8bbe22 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -93,7 +88,7 @@ namespace Godot
case 2:
return origin;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -110,7 +105,7 @@ namespace Godot
origin = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -389,31 +384,6 @@ namespace Godot
return copy;
}
- /// <summary>
- /// Returns a vector transformed (multiplied) by this transformation matrix.
- /// </summary>
- /// <seealso cref="XformInv(Vector2)"/>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- [Obsolete("Xform is deprecated. Use the multiplication operator (Transform2D * Vector2) instead.")]
- public Vector2 Xform(Vector2 v)
- {
- return new Vector2(Tdotx(v), Tdoty(v)) + origin;
- }
-
- /// <summary>
- /// Returns a vector transformed (multiplied) by the inverse transformation matrix.
- /// </summary>
- /// <seealso cref="Xform(Vector2)"/>
- /// <param name="v">A vector to inversely transform.</param>
- /// <returns>The inversely transformed vector.</returns>
- [Obsolete("XformInv is deprecated. Use the multiplication operator (Vector2 * Transform2D) instead.")]
- public Vector2 XformInv(Vector2 v)
- {
- Vector2 vInv = v - origin;
- return new Vector2(x.Dot(vInv), y.Dot(vInv));
- }
-
// Constants
private static readonly Transform2D _identity = new Transform2D(1, 0, 0, 1, 0, 0);
private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0);
@@ -507,7 +477,7 @@ namespace Godot
}
/// <summary>
- /// Returns a Vector2 transformed (multiplied) by transformation matrix.
+ /// Returns a Vector2 transformed (multiplied) by the transformation matrix.
/// </summary>
/// <param name="transform">The transformation to apply.</param>
/// <param name="vector">A Vector2 to transform.</param>
@@ -530,7 +500,7 @@ namespace Godot
}
/// <summary>
- /// Returns a Rect2 transformed (multiplied) by transformation matrix.
+ /// Returns a Rect2 transformed (multiplied) by the transformation matrix.
/// </summary>
/// <param name="transform">The transformation to apply.</param>
/// <param name="rect">A Rect2 to transform.</param>
@@ -541,7 +511,7 @@ namespace Godot
Vector2 toX = transform.x * rect.Size.x;
Vector2 toY = transform.y * rect.Size.y;
- return new Rect2(pos, rect.Size).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY);
+ return new Rect2(pos, new Vector2()).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY);
}
/// <summary>
@@ -557,11 +527,11 @@ namespace Godot
Vector2 to2 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y + rect.Size.y) * transform;
Vector2 to3 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y) * transform;
- return new Rect2(pos, rect.Size).Expand(to1).Expand(to2).Expand(to3);
+ return new Rect2(pos, new Vector2()).Expand(to1).Expand(to2).Expand(to3);
}
/// <summary>
- /// Returns a copy of the given Vector2[] transformed (multiplied) by transformation matrix.
+ /// Returns a copy of the given Vector2[] transformed (multiplied) by the transformation matrix.
/// </summary>
/// <param name="transform">The transformation to apply.</param>
/// <param name="array">A Vector2[] to transform.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 9eaf4f3252..5481225e3f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -52,7 +47,7 @@ namespace Godot
case 3:
return origin;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -72,7 +67,7 @@ namespace Godot
origin = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -113,7 +108,7 @@ namespace Godot
public Transform3D AffineInverse()
{
Basis basisInv = basis.Inverse();
- return new Transform3D(basisInv, basisInv.Xform(-origin));
+ return new Transform3D(basisInv, basisInv * -origin);
}
/// <summary>
@@ -152,7 +147,7 @@ namespace Godot
public Transform3D Inverse()
{
Basis basisTr = basis.Transposed();
- return new Transform3D(basisTr, basisTr.Xform(-origin));
+ return new Transform3D(basisTr, basisTr * -origin);
}
/// <summary>
@@ -168,7 +163,7 @@ namespace Godot
/// <param name="target">The object to look at.</param>
/// <param name="up">The relative up direction.</param>
/// <returns>The resulting transform.</returns>
- public Transform3D LookingAt(Vector3 target, Vector3 up)
+ public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
{
Transform3D t = this;
t.SetLookAt(origin, target, up);
@@ -194,7 +189,7 @@ namespace Godot
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
/// <param name="angle">The angle to rotate, in radians.</param>
/// <returns>The rotated transformation matrix.</returns>
- public Transform3D Rotated(Vector3 axis, real_t angle)
+ public readonly Transform3D Rotated(Vector3 axis, real_t angle)
{
return new Transform3D(new Basis(axis, angle), new Vector3()) * this;
}
@@ -291,43 +286,6 @@ namespace Godot
));
}
- /// <summary>
- /// Returns a vector transformed (multiplied) by this transformation matrix.
- /// </summary>
- /// <seealso cref="XformInv(Vector3)"/>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public Vector3 Xform(Vector3 v)
- {
- return new Vector3
- (
- basis.Row0.Dot(v) + origin.x,
- basis.Row1.Dot(v) + origin.y,
- basis.Row2.Dot(v) + origin.z
- );
- }
-
- /// <summary>
- /// Returns a vector transformed (multiplied) by the transposed transformation matrix.
- ///
- /// Note: This results in a multiplication by the inverse of the
- /// transformation matrix only if it represents a rotation-reflection.
- /// </summary>
- /// <seealso cref="Xform(Vector3)"/>
- /// <param name="v">A vector to inversely transform.</param>
- /// <returns>The inversely transformed vector.</returns>
- public Vector3 XformInv(Vector3 v)
- {
- Vector3 vInv = v - origin;
-
- return new Vector3
- (
- (basis.Row0[0] * vInv.x) + (basis.Row1[0] * vInv.y) + (basis.Row2[0] * vInv.z),
- (basis.Row0[1] * vInv.x) + (basis.Row1[1] * vInv.y) + (basis.Row2[1] * vInv.z),
- (basis.Row0[2] * vInv.x) + (basis.Row1[2] * vInv.y) + (basis.Row2[2] * vInv.z)
- );
- }
-
// Constants
private static readonly Transform3D _identity = new Transform3D(Basis.Identity, Vector3.Zero);
private static readonly Transform3D _flipX = new Transform3D(new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1), Vector3.Zero);
@@ -404,12 +362,188 @@ namespace Godot
/// <returns>The composed transform.</returns>
public static Transform3D operator *(Transform3D left, Transform3D right)
{
- left.origin = left.Xform(right.origin);
+ left.origin = left * right.origin;
left.basis *= right.basis;
return left;
}
/// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The transformed Vector3.</returns>
+ public static Vector3 operator *(Transform3D transform, Vector3 vector)
+ {
+ return new Vector3
+ (
+ transform.basis.Row0.Dot(vector) + transform.origin.x,
+ transform.basis.Row1.Dot(vector) + transform.origin.y,
+ transform.basis.Row2.Dot(vector) + transform.origin.z
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the transposed transformation matrix.
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// transformation matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="vector">A Vector3 to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed Vector3.</returns>
+ public static Vector3 operator *(Vector3 vector, Transform3D transform)
+ {
+ Vector3 vInv = vector - transform.origin;
+
+ return new Vector3
+ (
+ (transform.basis.Row0[0] * vInv.x) + (transform.basis.Row1[0] * vInv.y) + (transform.basis.Row2[0] * vInv.z),
+ (transform.basis.Row0[1] * vInv.x) + (transform.basis.Row1[1] * vInv.y) + (transform.basis.Row2[1] * vInv.z),
+ (transform.basis.Row0[2] * vInv.x) + (transform.basis.Row1[2] * vInv.y) + (transform.basis.Row2[2] * vInv.z)
+ );
+ }
+
+ /// <summary>
+ /// Returns an AABB transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="aabb">An AABB to transform.</param>
+ /// <returns>The transformed AABB.</returns>
+ public static AABB operator *(Transform3D transform, AABB aabb)
+ {
+ Vector3 min = aabb.Position;
+ Vector3 max = aabb.Position + aabb.Size;
+
+ Vector3 tmin = transform.origin;
+ Vector3 tmax = transform.origin;
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ real_t e = transform.basis[i][j] * min[j];
+ real_t f = transform.basis[i][j] * max[j];
+ if (e < f)
+ {
+ tmin[i] += e;
+ tmax[i] += f;
+ }
+ else
+ {
+ tmin[i] += f;
+ tmax[i] += e;
+ }
+ }
+ }
+
+ return new AABB(tmin, tmax - tmin);
+ }
+
+ /// <summary>
+ /// Returns an AABB transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="aabb">An AABB to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed AABB.</returns>
+ public static AABB operator *(AABB aabb, Transform3D transform)
+ {
+ Vector3 pos = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y + aabb.Size.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to1 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y + aabb.Size.y, aabb.Position.z) * transform;
+ Vector3 to2 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to3 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y, aabb.Position.z) * transform;
+ Vector3 to4 = new Vector3(aabb.Position.x, aabb.Position.y + aabb.Size.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to5 = new Vector3(aabb.Position.x, aabb.Position.y + aabb.Size.y, aabb.Position.z) * transform;
+ Vector3 to6 = new Vector3(aabb.Position.x, aabb.Position.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to7 = new Vector3(aabb.Position.x, aabb.Position.y, aabb.Position.z) * transform;
+
+ return new AABB(pos, new Vector3()).Expand(to1).Expand(to2).Expand(to3).Expand(to4).Expand(to5).Expand(to6).Expand(to7);
+ }
+
+ /// <summary>
+ /// Returns a Plane transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="plane">A Plane to transform.</param>
+ /// <returns>The transformed Plane.</returns>
+ public static Plane operator *(Transform3D transform, Plane plane)
+ {
+ Basis bInvTrans = transform.basis.Inverse().Transposed();
+
+ // Transform a single point on the plane.
+ Vector3 point = transform * (plane.Normal * plane.D);
+
+ // Use inverse transpose for correct normals with non-uniform scaling.
+ Vector3 normal = (bInvTrans * plane.Normal).Normalized();
+
+ real_t d = normal.Dot(point);
+ return new Plane(normal, d);
+ }
+
+ /// <summary>
+ /// Returns a Plane transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="plane">A Plane to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed Plane.</returns>
+ public static Plane operator *(Plane plane, Transform3D transform)
+ {
+ Transform3D tInv = transform.AffineInverse();
+ Basis bTrans = transform.basis.Transposed();
+
+ // Transform a single point on the plane.
+ Vector3 point = tInv * (plane.Normal * plane.D);
+
+ // Note that instead of precalculating the transpose, an alternative
+ // would be to use the transpose for the basis transform.
+ // However that would be less SIMD friendly (requiring a swizzle).
+ // So the cost is one extra precalced value in the calling code.
+ // This is probably worth it, as this could be used in bottleneck areas. And
+ // where it is not a bottleneck, the non-fast method is fine.
+
+ // Use transpose for correct normals with non-uniform scaling.
+ Vector3 normal = (bTrans * plane.Normal).Normalized();
+
+ real_t d = normal.Dot(point);
+ return new Plane(normal, d);
+ }
+
+ /// <summary>
+ /// Returns a copy of the given Vector3[] transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="array">A Vector3[] to transform.</param>
+ /// <returns>The transformed copy of the Vector3[].</returns>
+ public static Vector3[] operator *(Transform3D transform, Vector3[] array)
+ {
+ Vector3[] newArray = new Vector3[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ newArray[i] = transform * array[i];
+ }
+
+ return newArray;
+ }
+
+ /// <summary>
+ /// Returns a copy of the given Vector3[] transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="array">A Vector3[] to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed copy of the Vector3[].</returns>
+ public static Vector3[] operator *(Vector3[] array, Transform3D transform)
+ {
+ Vector3[] newArray = new Vector3[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ newArray[i] = array[i] * transform;
+ }
+
+ return newArray;
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the transforms are exactly equal.
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
@@ -443,7 +577,7 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the transform and the object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
if (obj is Transform3D)
{
@@ -460,7 +594,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are exactly equal.</returns>
- public bool Equals(Transform3D other)
+ public readonly bool Equals(Transform3D other)
{
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
deleted file mode 100644
index eae8927ceb..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// Event arguments for when unhandled exceptions occur.
- /// </summary>
- public class UnhandledExceptionArgs
- {
- /// <summary>
- /// Exception object.
- /// </summary>
- public Exception Exception { get; private set; }
-
- internal UnhandledExceptionArgs(Exception exception)
- {
- Exception = exception;
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 67f70390dd..04c2ea7eb9 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -62,7 +57,7 @@ namespace Godot
case 1:
return y;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -76,7 +71,7 @@ namespace Godot
y = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -644,16 +639,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector2"/> from an existing <see cref="Vector2"/>.
- /// </summary>
- /// <param name="v">The existing <see cref="Vector2"/>.</param>
- public Vector2(Vector2 v)
- {
- x = v.x;
- y = v.y;
- }
-
- /// <summary>
/// Creates a unit Vector2 rotated to the given angle. This is equivalent to doing
/// <c>Vector2(Mathf.Cos(angle), Mathf.Sin(angle))</c> or <c>Vector2.Right.Rotated(angle)</c>.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index b61954a84c..a8f42972d7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -62,7 +57,7 @@ namespace Godot
case 1:
return y;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -76,7 +71,7 @@ namespace Godot
y = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -355,27 +350,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector2i"/> from an existing <see cref="Vector2i"/>.
- /// </summary>
- /// <param name="vi">The existing <see cref="Vector2i"/>.</param>
- public Vector2i(Vector2i vi)
- {
- this.x = vi.x;
- this.y = vi.y;
- }
-
- /// <summary>
- /// Constructs a new <see cref="Vector2i"/> from an existing <see cref="Vector2"/>
- /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
- /// </summary>
- /// <param name="v">The <see cref="Vector2"/> to convert.</param>
- public Vector2i(Vector2 v)
- {
- this.x = Mathf.RoundToInt(v.x);
- this.y = Mathf.RoundToInt(v.y);
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector2i"/>
/// with the components of the given <see cref="Vector2i"/>.
/// </summary>
@@ -679,7 +653,10 @@ namespace Godot
/// <param name="value">The vector to convert.</param>
public static explicit operator Vector2i(Vector2 value)
{
- return new Vector2i(value);
+ return new Vector2i(
+ Mathf.RoundToInt(value.x),
+ Mathf.RoundToInt(value.y)
+ );
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 67a98efc2d..d5941d6b60 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -74,7 +69,7 @@ namespace Godot
case 2:
return z;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -91,7 +86,7 @@ namespace Godot
z = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -523,7 +518,7 @@ namespace Godot
throw new ArgumentException("Argument is not normalized", nameof(axis));
}
#endif
- return new Basis(axis, angle).Xform(this);
+ return new Basis(axis, angle) * this;
}
/// <summary>
@@ -697,17 +692,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector3"/> from an existing <see cref="Vector3"/>.
- /// </summary>
- /// <param name="v">The existing <see cref="Vector3"/>.</param>
- public Vector3(Vector3 v)
- {
- x = v.x;
- y = v.y;
- z = v.z;
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector3"/>
/// with the components of the given <see cref="Vector3"/>.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index 0d4894f206..eb46f36e7c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -74,7 +69,7 @@ namespace Godot
case 2:
return z;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -91,7 +86,7 @@ namespace Godot
z = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -335,29 +330,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector3i"/> from an existing <see cref="Vector3i"/>.
- /// </summary>
- /// <param name="vi">The existing <see cref="Vector3i"/>.</param>
- public Vector3i(Vector3i vi)
- {
- this.x = vi.x;
- this.y = vi.y;
- this.z = vi.z;
- }
-
- /// <summary>
- /// Constructs a new <see cref="Vector3i"/> from an existing <see cref="Vector3"/>
- /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
- /// </summary>
- /// <param name="v">The <see cref="Vector3"/> to convert.</param>
- public Vector3i(Vector3 v)
- {
- this.x = Mathf.RoundToInt(v.x);
- this.y = Mathf.RoundToInt(v.y);
- this.z = Mathf.RoundToInt(v.z);
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector3i"/>
/// with the components of the given <see cref="Vector3i"/>.
/// </summary>
@@ -689,7 +661,11 @@ namespace Godot
/// <param name="value">The vector to convert.</param>
public static explicit operator Vector3i(Vector3 value)
{
- return new Vector3i(value);
+ return new Vector3i(
+ Mathf.RoundToInt(value.x),
+ Mathf.RoundToInt(value.y),
+ Mathf.RoundToInt(value.z)
+ );
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
index 4af817455c..20a24616ce 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -482,18 +477,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector4"/> from an existing <see cref="Vector4"/>.
- /// </summary>
- /// <param name="v">The existing <see cref="Vector4"/>.</param>
- public Vector4(Vector4 v)
- {
- x = v.x;
- y = v.y;
- z = v.z;
- w = v.w;
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector4"/>
/// with the components of the given <see cref="Vector4"/>.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
index 365dcef486..11c2b7234b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -263,31 +258,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector4i"/> from an existing <see cref="Vector4i"/>.
- /// </summary>
- /// <param name="vi">The existing <see cref="Vector4i"/>.</param>
- public Vector4i(Vector4i vi)
- {
- this.x = vi.x;
- this.y = vi.y;
- this.z = vi.z;
- this.w = vi.w;
- }
-
- /// <summary>
- /// Constructs a new <see cref="Vector4i"/> from an existing <see cref="Vector4"/>
- /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
- /// </summary>
- /// <param name="v">The <see cref="Vector4"/> to convert.</param>
- public Vector4i(Vector4 v)
- {
- this.x = Mathf.RoundToInt(v.x);
- this.y = Mathf.RoundToInt(v.y);
- this.z = Mathf.RoundToInt(v.z);
- this.w = Mathf.RoundToInt(v.w);
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector4i"/>
/// with the components of the given <see cref="Vector4i"/>.
/// </summary>
@@ -643,7 +613,12 @@ namespace Godot
/// <param name="value">The vector to convert.</param>
public static explicit operator Vector4i(Vector4 value)
{
- return new Vector4i(value);
+ return new Vector4i(
+ Mathf.RoundToInt(value.x),
+ Mathf.RoundToInt(value.y),
+ Mathf.RoundToInt(value.z),
+ Mathf.RoundToInt(value.w)
+ );
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs b/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs
new file mode 100644
index 0000000000..263a934fae
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs
@@ -0,0 +1,5 @@
+#if REAL_T_IS_DOUBLE
+global using real_t = System.Double;
+#else
+global using real_t = System.Single;
+#endif
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 4f55ce47e8..aae7a5ebfa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -4,25 +4,71 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <LangVersion>10</LangVersion>
+
+ <AnalysisMode>Recommended</AnalysisMode>
+
+ <!-- Disabled temporarily as it pollutes the warnings, but we need to document public APIs. -->
+ <NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
+ <Description>Godot C# Core API.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>GodotSharp</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ </PropertyGroup>
+ <ItemGroup>
+ <!-- SdkPackageVersions.props for easy access -->
+ <None Include="$(GodotSdkPackageVersionsFilePath)">
+ <Link>SdkPackageVersions.props</Link>
+ </None>
+ </ItemGroup>
+ <PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="ReflectionAnalyzers" Version="0.1.22-dev" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" />
+ <!--PackageReference Include="IDisposableAnalyzers" Version="3.4.13" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" /-->
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+ <!-- Sources -->
+ <ItemGroup>
<Compile Include="Core\AABB.cs" />
+ <Compile Include="Core\Bridge\GodotSerializationInfo.cs" />
+ <Compile Include="Core\Bridge\MethodInfo.cs" />
+ <Compile Include="Core\CustomGCHandle.cs" />
<Compile Include="Core\Array.cs" />
<Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />
- <Compile Include="Core\Attributes\DisableGodotGeneratorsAttribute.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
- <Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
+ <Compile Include="Core\Attributes\ExportCategoryAttribute.cs" />
+ <Compile Include="Core\Attributes\ExportGroupAttribute.cs" />
+ <Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" />
+ <Compile Include="Core\Attributes\MustBeVariantAttribute.cs" />
<Compile Include="Core\Attributes\RPCAttribute.cs" />
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
<Compile Include="Core\Basis.cs" />
+ <Compile Include="Core\Bridge\CSharpInstanceBridge.cs" />
+ <Compile Include="Core\Bridge\GCHandleBridge.cs" />
+ <Compile Include="Core\Bridge\AlcReloadCfg.cs" />
+ <Compile Include="Core\Bridge\ManagedCallbacks.cs" />
+ <Compile Include="Core\Bridge\PropertyInfo.cs" />
+ <Compile Include="Core\Bridge\ScriptManagerBridge.cs" />
+ <Compile Include="Core\Bridge\ScriptManagerBridge.types.cs" />
<Compile Include="Core\Callable.cs" />
<Compile Include="Core\Color.cs" />
<Compile Include="Core\Colors.cs" />
@@ -30,45 +76,58 @@
<Compile Include="Core\DelegateUtils.cs" />
<Compile Include="Core\Dictionary.cs" />
<Compile Include="Core\Dispatcher.cs" />
- <Compile Include="Core\DynamicObject.cs" />
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
<Compile Include="Core\Extensions\PackedSceneExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
- <Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
<Compile Include="Core\GodotSynchronizationContext.cs" />
<Compile Include="Core\GodotTaskScheduler.cs" />
<Compile Include="Core\GodotTraceListener.cs" />
<Compile Include="Core\GodotUnhandledExceptionEvent.cs" />
+ <Compile Include="Core\DisposablesTracker.cs" />
<Compile Include="Core\Interfaces\IAwaitable.cs" />
<Compile Include="Core\Interfaces\IAwaiter.cs" />
<Compile Include="Core\Interfaces\ISerializationListener.cs" />
- <Compile Include="Core\MarshalUtils.cs" />
<Compile Include="Core\Mathf.cs" />
<Compile Include="Core\MathfEx.cs" />
+ <Compile Include="Core\NativeInterop\CustomUnsafe.cs" />
+ <Compile Include="Core\NativeInterop\ExceptionUtils.cs" />
+ <Compile Include="Core\NativeInterop\GodotDllImportResolver.cs" />
+ <Compile Include="Core\NativeInterop\InteropUtils.cs" />
+ <Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" />
+ <Compile Include="Core\NativeInterop\NativeVariantPtrArgs.cs" />
+ <Compile Include="Core\NativeInterop\VariantConversionCallbacks.cs" />
+ <Compile Include="Core\NativeInterop\VariantSpanHelpers.cs" />
+ <Compile Include="Core\NativeInterop\VariantUtils.cs" />
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
+ <Compile Include="Core\Object.exceptions.cs" />
<Compile Include="Core\Plane.cs" />
<Compile Include="Core\Projection.cs" />
<Compile Include="Core\Quaternion.cs" />
<Compile Include="Core\Rect2.cs" />
<Compile Include="Core\Rect2i.cs" />
+ <Compile Include="Core\ReflectionUtils.cs" />
<Compile Include="Core\RID.cs" />
+ <Compile Include="Core\NativeInterop\NativeFuncs.cs" />
+ <Compile Include="Core\NativeInterop\InteropStructs.cs" />
+ <Compile Include="Core\NativeInterop\Marshaling.cs" />
<Compile Include="Core\SignalInfo.cs" />
<Compile Include="Core\SignalAwaiter.cs" />
<Compile Include="Core\StringExtensions.cs" />
<Compile Include="Core\StringName.cs" />
<Compile Include="Core\Transform2D.cs" />
<Compile Include="Core\Transform3D.cs" />
- <Compile Include="Core\UnhandledExceptionArgs.cs" />
<Compile Include="Core\Vector2.cs" />
<Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
<Compile Include="Core\Vector3i.cs" />
<Compile Include="Core\Vector4.cs" />
<Compile Include="Core\Vector4i.cs" />
+ <Compile Include="GlobalUsings.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Variant.cs" />
</ItemGroup>
<!--
We import a props file with auto-generated includes. This works well with Rider.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings
new file mode 100644
index 0000000000..1add6cc77e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings
@@ -0,0 +1,5 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=core/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
+</wpf:ResourceDictionary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs
new file mode 100644
index 0000000000..85ef258922
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs
@@ -0,0 +1,841 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
+
+namespace Godot;
+
+#nullable enable
+
+[SuppressMessage("ReSharper", "RedundantNameQualifier")]
+public partial struct Variant : IDisposable
+{
+ internal godot_variant.movable NativeVar;
+ private object? _obj;
+ private Disposer? _disposer;
+
+ private sealed class Disposer : IDisposable
+ {
+ private godot_variant.movable _native;
+
+ private WeakReference<IDisposable>? _weakReferenceToSelf;
+
+ public Disposer(in godot_variant.movable nativeVar)
+ {
+ _native = nativeVar;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
+
+ ~Disposer()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ _native.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
+ {
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+ }
+ }
+ }
+
+ private Variant(in godot_variant nativeVar)
+ {
+ NativeVar = (godot_variant.movable)nativeVar;
+ _obj = null;
+
+ switch (nativeVar.Type)
+ {
+ case Type.Nil:
+ case Type.Bool:
+ case Type.Int:
+ case Type.Float:
+ case Type.Vector2:
+ case Type.Vector2i:
+ case Type.Rect2:
+ case Type.Rect2i:
+ case Type.Vector3:
+ case Type.Vector3i:
+ case Type.Plane:
+ case Type.Quaternion:
+ case Type.Color:
+ case Type.Rid:
+ _disposer = null;
+ break;
+ default:
+ {
+ _disposer = new Disposer(NativeVar);
+ break;
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // Explicit name to make it very clear
+ public static Variant CreateTakingOwnershipOfDisposableValue(in godot_variant nativeValueToOwn) =>
+ new(nativeValueToOwn);
+
+ // Explicit name to make it very clear
+ public static Variant CreateCopyingBorrowed(in godot_variant nativeValueToOwn) =>
+ new(NativeFuncs.godotsharp_variant_new_copy(nativeValueToOwn));
+
+ /// <summary>
+ /// Constructs a new <see cref="Godot.NativeInterop.godot_variant"/> from this instance.
+ /// The caller is responsible of disposing the new instance to avoid memory leaks.
+ /// </summary>
+ public godot_variant CopyNativeVariant() =>
+ NativeFuncs.godotsharp_variant_new_copy((godot_variant)NativeVar);
+
+ public void Dispose()
+ {
+ _disposer?.Dispose();
+ NativeVar = default;
+ _obj = null;
+ }
+
+ // TODO: Consider renaming Variant.Type to VariantType and this property to Type. VariantType would also avoid ambiguity with System.Type.
+ public Type VariantType => NativeVar.DangerousSelfRef.Type;
+
+ public override string ToString() => AsString();
+
+ public object? Obj
+ {
+ get
+ {
+ if (_obj == null)
+ _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar);
+
+ return _obj;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool AsBool() =>
+ VariantUtils.ConvertToBool((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public char AsChar() =>
+ (char)VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public sbyte AsSByte() =>
+ VariantUtils.ConvertToInt8((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public short AsInt16() =>
+ VariantUtils.ConvertToInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int AsInt32() =>
+ VariantUtils.ConvertToInt32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long AsInt64() =>
+ VariantUtils.ConvertToInt64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte AsByte() =>
+ VariantUtils.ConvertToUInt8((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ushort AsUInt16() =>
+ VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint AsUInt32() =>
+ VariantUtils.ConvertToUInt32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ulong AsUInt64() =>
+ VariantUtils.ConvertToUInt64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float AsSingle() =>
+ VariantUtils.ConvertToFloat32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double AsDouble() =>
+ VariantUtils.ConvertToFloat64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string AsString() =>
+ VariantUtils.ConvertToStringObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2 AsVector2() =>
+ VariantUtils.ConvertToVector2((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2i AsVector2i() =>
+ VariantUtils.ConvertToVector2i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rect2 AsRect2() =>
+ VariantUtils.ConvertToRect2((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rect2i AsRect2i() =>
+ VariantUtils.ConvertToRect2i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Transform2D AsTransform2D() =>
+ VariantUtils.ConvertToTransform2D((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3 AsVector3() =>
+ VariantUtils.ConvertToVector3((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3i AsVector3i() =>
+ VariantUtils.ConvertToVector3i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Basis AsBasis() =>
+ VariantUtils.ConvertToBasis((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Quaternion AsQuaternion() =>
+ VariantUtils.ConvertToQuaternion((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Transform3D AsTransform3D() =>
+ VariantUtils.ConvertToTransform3D((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 AsVector4() =>
+ VariantUtils.ConvertToVector4((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4i AsVector4i() =>
+ VariantUtils.ConvertToVector4i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Projection AsProjection() =>
+ VariantUtils.ConvertToProjection((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AABB AsAABB() =>
+ VariantUtils.ConvertToAABB((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Color AsColor() =>
+ VariantUtils.ConvertToColor((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Plane AsPlane() =>
+ VariantUtils.ConvertToPlane((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Callable AsCallable() =>
+ VariantUtils.ConvertToCallableManaged((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public SignalInfo AsSignalInfo() =>
+ VariantUtils.ConvertToSignalInfo((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte[] AsByteArray() =>
+ VariantUtils.ConvertAsPackedByteArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int[] AsInt32Array() =>
+ VariantUtils.ConvertAsPackedInt32ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long[] AsInt64Array() =>
+ VariantUtils.ConvertAsPackedInt64ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float[] AsFloat32Array() =>
+ VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double[] AsFloat64Array() =>
+ VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string[] AsStringArray() =>
+ VariantUtils.ConvertAsPackedStringArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2[] AsVector2Array() =>
+ VariantUtils.ConvertAsPackedVector2ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3[] AsVector3Array() =>
+ VariantUtils.ConvertAsPackedVector3ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Color[] AsColorArray() =>
+ VariantUtils.ConvertAsPackedColorArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T[] AsGodotObjectArray<T>()
+ where T : Godot.Object =>
+ VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Dictionary<TKey, TValue> AsGodotDictionary<TKey, TValue>() =>
+ VariantUtils.ConvertToDictionaryObject<TKey, TValue>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Array<T> AsGodotArray<T>() =>
+ VariantUtils.ConvertToArrayObject<T>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public StringName[] AsSystemArrayOfStringName() =>
+ VariantUtils.ConvertToSystemArrayOfStringName((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public NodePath[] AsSystemArrayOfNodePath() =>
+ VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RID[] AsSystemArrayOfRID() =>
+ VariantUtils.ConvertToSystemArrayOfRID((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Godot.Object AsGodotObject() =>
+ VariantUtils.ConvertToGodotObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public StringName AsStringName() =>
+ VariantUtils.ConvertToStringNameObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public NodePath AsNodePath() =>
+ VariantUtils.ConvertToNodePathObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RID AsRID() =>
+ VariantUtils.ConvertToRID((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Dictionary AsGodotDictionary() =>
+ VariantUtils.ConvertToDictionaryObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Array AsGodotArray() =>
+ VariantUtils.ConvertToArrayObject((godot_variant)NativeVar);
+
+ // Explicit conversion operators to supported types
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator bool(Variant from) => from.AsBool();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator char(Variant from) => from.AsChar();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator sbyte(Variant from) => from.AsSByte();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator short(Variant from) => from.AsInt16();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator int(Variant from) => from.AsInt32();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator long(Variant from) => from.AsInt64();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator byte(Variant from) => from.AsByte();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator ushort(Variant from) => from.AsUInt16();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator uint(Variant from) => from.AsUInt32();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator ulong(Variant from) => from.AsUInt64();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator float(Variant from) => from.AsSingle();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator double(Variant from) => from.AsDouble();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator string(Variant from) => from.AsString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2(Variant from) => from.AsVector2();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2i(Variant from) => from.AsVector2i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Rect2(Variant from) => from.AsRect2();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Rect2i(Variant from) => from.AsRect2i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Transform2D(Variant from) => from.AsTransform2D();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3(Variant from) => from.AsVector3();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3i(Variant from) => from.AsVector3i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Basis(Variant from) => from.AsBasis();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Quaternion(Variant from) => from.AsQuaternion();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Transform3D(Variant from) => from.AsTransform3D();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector4(Variant from) => from.AsVector4();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector4i(Variant from) => from.AsVector4i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Projection(Variant from) => from.AsProjection();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator AABB(Variant from) => from.AsAABB();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Color(Variant from) => from.AsColor();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Plane(Variant from) => from.AsPlane();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Callable(Variant from) => from.AsCallable();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator SignalInfo(Variant from) => from.AsSignalInfo();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator byte[](Variant from) => from.AsByteArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator int[](Variant from) => from.AsInt32Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator long[](Variant from) => from.AsInt64Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator float[](Variant from) => from.AsFloat32Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator double[](Variant from) => from.AsFloat64Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator string[](Variant from) => from.AsStringArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2[](Variant from) => from.AsVector2Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3[](Variant from) => from.AsVector3Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Color[](Variant from) => from.AsColorArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator StringName[](Variant from) => from.AsSystemArrayOfStringName();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator RID[](Variant from) => from.AsSystemArrayOfRID();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Godot.Object(Variant from) => from.AsGodotObject();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator StringName(Variant from) => from.AsStringName();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator NodePath(Variant from) => from.AsNodePath();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator RID(Variant from) => from.AsRID();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Collections.Dictionary(Variant from) => from.AsGodotDictionary();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Collections.Array(Variant from) => from.AsGodotArray();
+
+ // While we provide implicit conversion operators, normal methods are still needed for
+ // casts that are not done implicitly (e.g.: raw array to Span, enum to integer, etc).
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(bool from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(char from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(sbyte from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(short from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(int from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(long from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(byte from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(ushort from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(uint from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(ulong from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(float from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(double from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(string from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector2 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector2i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Rect2 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Rect2i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Transform2D from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector3 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector3i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Basis from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Quaternion from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Transform3D from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector4 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector4i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Projection from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(AABB from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Color from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Plane from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Callable from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(SignalInfo from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<byte> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<int> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<long> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<float> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<double> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<string> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Vector2> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Vector3> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Color> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Godot.Object[] from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom<T>(Collections.Array<T> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<StringName> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<NodePath> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<RID> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Godot.Object from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(StringName from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(NodePath from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(RID from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Collections.Dictionary from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Collections.Array from) => from;
+
+ // Implicit conversion operators
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(bool from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBool(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(char from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(sbyte from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(short from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(int from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(long from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(byte from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(ushort from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(uint from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(ulong from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(float from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(double from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(string from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromString(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector2 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector2i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Rect2 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Rect2i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Transform2D from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform2D(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector3 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector3i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Basis from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBasis(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Quaternion from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromQuaternion(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Transform3D from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform3D(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector4 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector4i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Projection from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(AABB from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAABB(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Color from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromColor(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Plane from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPlane(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Callable from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromCallable(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(SignalInfo from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSignalInfo(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<byte> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedByteArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<int> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<long> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<float> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<double> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<string> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedStringArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<Vector2> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector2Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<Vector3> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector3Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<Color> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Godot.Object[] from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<StringName> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfStringName(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<NodePath> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<RID> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRID(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Godot.Object from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromGodotObject(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(StringName from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromStringName(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(NodePath from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromNodePath(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(RID from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRID(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Collections.Dictionary from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Collections.Array from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
index a8c4ba96b5..ebf09aab7b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
@@ -4,9 +4,24 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <LangVersion>10</LangVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <Description>Godot C# Editor API.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>GodotSharpEditor</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings
new file mode 100644
index 0000000000..c7ff6fd3ee
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings
@@ -0,0 +1,4 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
+</wpf:ResourceDictionary>
diff --git a/modules/mono/glue/arguments_vector.h b/modules/mono/glue/arguments_vector.h
deleted file mode 100644
index 4405809887..0000000000
--- 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 <typename T, int POOL_SIZE = 5>
-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
deleted file mode 100644
index 7b9dbc87cf..0000000000
--- a/modules/mono/glue/base_object_glue.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*************************************************************************/
-/* base_object_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/object/class_db.h"
-#include "core/object/ref_counted.h"
-#include "core/string/string_name.h"
-
-#include "../csharp_script.h"
-#include "../mono_gd/gd_mono_cache.h"
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_internals.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../mono_gd/gd_mono_utils.h"
-#include "../signal_awaiter_utils.h"
-#include "arguments_vector.h"
-
-Object *godot_icall_Object_Ctor(MonoObject *p_obj) {
- Object *instance = memnew(Object);
- 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);
-#endif
-
- if (p_ptr->get_script_instance()) {
- CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
- if (cs_instance) {
- if (!cs_instance->is_destructing_script_instance()) {
- cs_instance->mono_object_disposed(p_obj);
- p_ptr->set_script_instance(nullptr);
- }
- return;
- }
- }
-
- void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
-
- if (data) {
- CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
- if (script_binding.inited) {
- MonoGCHandleData &gchandle = script_binding.gchandle;
- if (!gchandle.is_released()) {
- CSharpLanguage::release_script_gchandle(p_obj, gchandle);
- }
- }
- }
-}
-
-void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_ptr == nullptr);
- // This is only called with RefCounted derived classes
- CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
-#endif
-
- RefCounted *rc = static_cast<RefCounted *>(p_ptr);
-
- if (rc->get_script_instance()) {
- CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance());
- if (cs_instance) {
- if (!cs_instance->is_destructing_script_instance()) {
- bool delete_owner;
- bool remove_script_instance;
-
- cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance);
-
- if (delete_owner) {
- memdelete(rc);
- } else if (remove_script_instance) {
- rc->set_script_instance(nullptr);
- }
- }
- return;
- }
- }
-
- // Unsafe refcount decrement. The managed instance also counts as a reference.
- // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
- CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc);
- if (rc->unreference()) {
- memdelete(rc);
- } else {
- void *data = CSharpLanguage::get_existing_instance_binding(rc);
-
- if (data) {
- CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
- if (script_binding.inited) {
- MonoGCHandleData &gchandle = script_binding.gchandle;
- if (!gchandle.is_released()) {
- CSharpLanguage::release_script_gchandle(p_obj, gchandle);
- }
- }
- }
- }
-}
-
-void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
- CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
- if (csharp_instance) {
- csharp_instance->connect_event_signals();
- }
-}
-
-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;
- }
-
- Ref<WeakRef> wref;
- RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
-
- if (rc) {
- Ref<RefCounted> 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);
-}
-
-MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
- List<PropertyInfo> property_list;
- p_ptr->get_property_list(&property_list);
-
- MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size());
-
- int i = 0;
- for (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<Variant> arg_store(argc);
- ArgumentsVector<const Variant *> args(argc);
-
- for (int i = 0; i < argc; i++) {
- MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
- arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
- args.set(i, &arg_store.get(i));
- }
-
- 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.
- 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_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);
- 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);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/callable_glue.cpp b/modules/mono/glue/callable_glue.cpp
deleted file mode 100644
index 521dc3dff7..0000000000
--- a/modules/mono/glue/callable_glue.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*************************************************************************/
-/* callable_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 "../mono_gd/gd_mono_marshal.h"
-#include "arguments_vector.h"
-
-MonoObject *godot_icall_Callable_Call(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
- Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
-
- int argc = mono_array_length(p_args);
-
- ArgumentsVector<Variant> arg_store(argc);
- ArgumentsVector<const Variant *> args(argc);
-
- for (int i = 0; i < argc; i++) {
- MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
- arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
- args.set(i, &arg_store.get(i));
- }
-
- Variant result;
- Callable::CallError error;
- callable.callp(args.ptr(), argc, result, error);
-
- return GDMonoMarshal::variant_to_mono_object(result);
-}
-
-void godot_icall_Callable_CallDeferred(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
- Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
-
- int argc = mono_array_length(p_args);
-
- ArgumentsVector<Variant> arg_store(argc);
- ArgumentsVector<const Variant *> args(argc);
-
- for (int i = 0; i < argc; i++) {
- MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
- arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
- args.set(i, &arg_store.get(i));
- }
-
- callable.call_deferredp(args.ptr(), argc);
-}
-
-void godot_register_callable_icalls() {
- GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_Call", godot_icall_Callable_Call);
- GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_CallDeferred", godot_icall_Callable_CallDeferred);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
deleted file mode 100644
index 8a9f30459c..0000000000
--- a/modules/mono/glue/collections_glue.cpp
+++ /dev/null
@@ -1,374 +0,0 @@
-/*************************************************************************/
-/* collections_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 <mono/metadata/exception.h>
-
-#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"
-
-Array *godot_icall_Array_Ctor() {
- return memnew(Array);
-}
-
-void godot_icall_Array_Dtor(Array *ptr) {
- memdelete(ptr);
-}
-
-MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) {
- if (index < 0 || index >= ptr->size()) {
- GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return nullptr;
- }
- return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
-}
-
-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));
-}
-
-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++;
- }
-}
-
-Array *godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array) {
- Array *godot_array = memnew(Array);
- unsigned int count = mono_array_length(mono_array);
- godot_array->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);
- }
- return godot_array;
-}
-
-Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) {
- return memnew(Array(ptr->duplicate(deep)));
-}
-
-Array *godot_icall_Array_Concatenate(Array *left, Array *right) {
- int count = left->size() + right->size();
- Array *new_array = memnew(Array(left->duplicate(false)));
- new_array->resize(count);
- for (unsigned int i = 0; i < (unsigned int)right->size(); i++) {
- new_array->operator[](i + left->size()) = right->operator[](i);
- }
- return new_array;
-}
-
-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();
-}
-
-void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
- MonoType *elem_type = mono_reflection_type_get_type(refltype);
-
- *type_encoding = mono_type_get_type(elem_type);
- MonoClass *type_class_raw = mono_class_from_mono_type(elem_type);
- *type_class = GDMono::get_singleton()->get_class(type_class_raw);
-}
-
-MonoString *godot_icall_Array_ToString(Array *ptr) {
- return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
-}
-
-Dictionary *godot_icall_Dictionary_Ctor() {
- return memnew(Dictionary);
-}
-
-void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
- memdelete(ptr);
-}
-
-MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
- Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == nullptr) {
- MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
-#ifdef DEBUG_ENABLED
- CRASH_COND(!exc);
-#endif
- GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
- GDMonoUtils::set_pending_exception((MonoException *)exc);
- return nullptr;
- }
- return GDMonoMarshal::variant_to_mono_object(ret);
-}
-
-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));
-}
-
-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()));
-}
-
-Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
- return memnew(Array(ptr->values()));
-}
-
-int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
- return ptr->size();
-}
-
-int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) {
- *keys = godot_icall_Dictionary_Keys(ptr);
- *values = godot_icall_Dictionary_Values(ptr);
- return godot_icall_Dictionary_Count(ptr);
-}
-
-void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
- *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index));
- *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index));
-}
-
-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_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));
-}
-
-Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep) {
- return memnew(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, MonoObject **value) {
- Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == nullptr) {
- *value = nullptr;
- return false;
- }
- *value = GDMonoMarshal::variant_to_mono_object(ret);
- return true;
-}
-
-MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) {
- 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());
-}
-
-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);
- 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_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);
- 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_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
deleted file mode 100644
index 8b1c2b729e..0000000000
--- a/modules/mono/glue/gd_glue.cpp
+++ /dev/null
@@ -1,348 +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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#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(MonoArray *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);
- 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));
-}
-
-MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) {
- 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).");
-
- barr.resize(len);
- encode_variant(var, barr.ptrw(), len, p_full_objects);
-
- return GDMonoMarshal::PackedByteArray_to_mono_array(barr);
-}
-
-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);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
deleted file mode 100644
index f9ad1a9893..0000000000
--- a/modules/mono/glue/glue_header.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* glue_header.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef GLUE_HEADER_H
-#define GLUE_HEADER_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-void godot_register_collections_icalls();
-void godot_register_gd_icalls();
-void godot_register_string_name_icalls();
-void godot_register_nodepath_icalls();
-void godot_register_callable_icalls();
-void godot_register_object_icalls();
-void godot_register_rid_icalls();
-void godot_register_string_icalls();
-void godot_register_scene_tree_icalls();
-
-/**
- * Registers internal calls that were not generated. This function is called
- * from the generated GodotSharpBindings::register_generated_icalls() function.
- */
-void godot_register_glue_header_icalls() {
- godot_register_collections_icalls();
- godot_register_gd_icalls();
- godot_register_string_name_icalls();
- godot_register_nodepath_icalls();
- godot_register_callable_icalls();
- godot_register_object_icalls();
- godot_register_rid_icalls();
- godot_register_string_icalls();
- godot_register_scene_tree_icalls();
-}
-
-// Used by the generated glue
-
-#include "core/config/engine.h"
-#include "core/object/class_db.h"
-#include "core/object/method_bind.h"
-#include "core/object/ref_counted.h"
-#include "core/string/node_path.h"
-#include "core/string/ustring.h"
-#include "core/typedefs.h"
-#include "core/variant/array.h"
-#include "core/variant/dictionary.h"
-
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_internals.h"
-#include "../mono_gd/gd_mono_utils.h"
-
-#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \
- static ClassDB::ClassInfo *ci = nullptr; \
- if (!ci) { \
- ci = ClassDB::classes.getptr(m_type); \
- } \
- Object *m_instance = ci->creation_func();
-
-#include "arguments_vector.h"
-
-#endif // MONO_GLUE_ENABLED
-
-#endif // GLUE_HEADER_H
diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp
deleted file mode 100644
index 16e1509eb0..0000000000
--- a/modules/mono/glue/nodepath_glue.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*************************************************************************/
-/* nodepath_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/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();
-}
-
-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());
-}
-
-NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr) {
- return memnew(NodePath(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);
- 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);
- 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/rid_glue.cpp
deleted file mode 100644
index 3e09564539..0000000000
--- a/modules/mono/glue/rid_glue.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*************************************************************************/
-/* rid_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/io/resource.h"
-#include "core/object/class_db.h"
-#include "core/templates/rid.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-RID *godot_icall_RID_Ctor(Object *p_from) {
- Resource *res_from = Object::cast_to<Resource>(p_from);
-
- if (res_from) {
- return memnew(RID(res_from->get_rid()));
- }
-
- return memnew(RID);
-}
-
-void godot_icall_RID_Dtor(RID *p_ptr) {
- ERR_FAIL_NULL(p_ptr);
- memdelete(p_ptr);
-}
-
-uint32_t godot_icall_RID_get_id(RID *p_ptr) {
- return p_ptr->get_id();
-}
-
-void godot_register_rid_icalls() {
- GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Ctor", godot_icall_RID_Ctor);
- GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Dtor", godot_icall_RID_Dtor);
- GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_get_id", godot_icall_RID_get_id);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
new file mode 100644
index 0000000000..13d4395a64
--- /dev/null
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -0,0 +1,1513 @@
+/*************************************************************************/
+/* runtime_interop.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 "runtime_interop.h"
+
+#include "core/config/engine.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.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"
+
+#include "modules/mono/csharp_script.h"
+#include "modules/mono/managed_callable.h"
+#include "modules/mono/mono_gd/gd_mono_cache.h"
+#include "modules/mono/signal_awaiter_utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// For ArrayPrivate and DictionaryPrivate
+static_assert(sizeof(SafeRefCount) == sizeof(uint32_t));
+
+typedef Object *(*godotsharp_class_creation_func)();
+
+MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const StringName *p_methodname) {
+ return ClassDB::get_method(*p_classname, *p_methodname);
+}
+
+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;
+}
+
+Object *godotsharp_engine_get_singleton(const String *p_name) {
+ return Engine::get_singleton()->get_singleton_object(*p_name);
+}
+
+int32_t godotsharp_stack_info_vector_resize(
+ Vector<ScriptLanguage::StackInfo> *p_stack_info_vector, int p_size) {
+ return (int32_t)p_stack_info_vector->resize(p_size);
+}
+
+void godotsharp_stack_info_vector_destroy(
+ Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) {
+ p_stack_info_vector->~Vector();
+}
+
+void godotsharp_internal_script_debugger_send_error(const String *p_func,
+ const String *p_file, int32_t p_line, const String *p_err, const String *p_descr,
+ bool p_warning, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) {
+ EngineDebugger::get_script_debugger()->send_error(*p_func, *p_file, p_line, *p_err, *p_descr,
+ true, p_warning ? ERR_HANDLER_WARNING : ERR_HANDLER_ERROR, *p_stack_info_vector);
+}
+
+bool godotsharp_internal_script_debugger_is_active() {
+ return EngineDebugger::is_active();
+}
+
+GCHandleIntPtr godotsharp_internal_object_get_associated_gchandle(Object *p_ptr) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_ptr == nullptr);
+#endif
+
+ if (p_ptr->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
+ if (cs_instance) {
+ if (!cs_instance->is_destructing_script_instance()) {
+ return cs_instance->get_gchandle_intptr();
+ }
+ return { nullptr };
+ }
+ }
+
+ void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
+
+ if (data) {
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
+ if (script_binding.inited) {
+ MonoGCHandleData &gchandle = script_binding.gchandle;
+ return !gchandle.is_released() ? gchandle.get_intptr() : GCHandleIntPtr{ nullptr };
+ }
+ }
+
+ return { nullptr };
+}
+
+void godotsharp_internal_object_disposed(Object *p_ptr, GCHandleIntPtr p_gchandle_to_free) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_ptr == nullptr);
+#endif
+
+ if (p_ptr->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
+ if (cs_instance) {
+ if (!cs_instance->is_destructing_script_instance()) {
+ cs_instance->mono_object_disposed(p_gchandle_to_free);
+ p_ptr->set_script_instance(nullptr);
+ }
+ return;
+ }
+ }
+
+ void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
+
+ if (data) {
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
+ if (script_binding.inited) {
+ if (!script_binding.gchandle.is_released()) {
+ CSharpLanguage::release_binding_gchandle_thread_safe(p_gchandle_to_free, script_binding);
+ }
+ }
+ }
+}
+
+void godotsharp_internal_refcounted_disposed(Object *p_ptr, GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_ptr == nullptr);
+ // This is only called with RefCounted derived classes
+ CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
+#endif
+
+ RefCounted *rc = static_cast<RefCounted *>(p_ptr);
+
+ if (rc->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance());
+ if (cs_instance) {
+ if (!cs_instance->is_destructing_script_instance()) {
+ bool delete_owner;
+ bool remove_script_instance;
+
+ cs_instance->mono_object_disposed_baseref(p_gchandle_to_free, p_is_finalizer,
+ delete_owner, remove_script_instance);
+
+ if (delete_owner) {
+ memdelete(rc);
+ } else if (remove_script_instance) {
+ rc->set_script_instance(nullptr);
+ }
+ }
+ return;
+ }
+ }
+
+ // Unsafe refcount decrement. The managed instance also counts as a reference.
+ // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
+ CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc);
+ if (rc->unreference()) {
+ memdelete(rc);
+ } else {
+ void *data = CSharpLanguage::get_existing_instance_binding(rc);
+
+ if (data) {
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
+ if (script_binding.inited) {
+ if (!script_binding.gchandle.is_released()) {
+ CSharpLanguage::release_binding_gchandle_thread_safe(p_gchandle_to_free, script_binding);
+ }
+ }
+ }
+ }
+}
+
+int32_t godotsharp_internal_signal_awaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
+ StringName signal = p_signal ? *p_signal : StringName();
+ return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr);
+}
+
+GCHandleIntPtr godotsharp_internal_unmanaged_get_script_instance_managed(Object *p_unmanaged, bool *r_has_cs_script_instance) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!p_unmanaged);
+ CRASH_COND(!r_has_cs_script_instance);
+#endif
+
+ if (p_unmanaged->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance());
+
+ if (cs_instance) {
+ *r_has_cs_script_instance = true;
+ return cs_instance->get_gchandle_intptr();
+ }
+ }
+
+ *r_has_cs_script_instance = false;
+ return { nullptr };
+}
+
+GCHandleIntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(Object *p_unmanaged) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!p_unmanaged);
+#endif
+
+ void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
+ ERR_FAIL_NULL_V(data, { nullptr });
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
+ ERR_FAIL_COND_V(!script_binding.inited, { nullptr });
+
+ return script_binding.gchandle.get_intptr();
+}
+
+GCHandleIntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(Object *p_unmanaged, GCHandleIntPtr p_old_gchandle) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!p_unmanaged);
+#endif
+
+ void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
+ ERR_FAIL_NULL_V(data, { nullptr });
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
+ ERR_FAIL_COND_V(!script_binding.inited, { nullptr });
+
+ MonoGCHandleData &gchandle = script_binding.gchandle;
+
+ // TODO: Possible data race?
+ CRASH_COND(gchandle.get_intptr().value != p_old_gchandle.value);
+
+ CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
+ script_binding.inited = false;
+
+ // Create a new one
+
+#ifdef DEBUG_ENABLED
+ CRASH_COND(script_binding.type_name == StringName());
+#endif
+
+ bool parent_is_object_class = ClassDB::is_parent_class(p_unmanaged->get_class_name(), script_binding.type_name);
+ ERR_FAIL_COND_V_MSG(!parent_is_object_class, { nullptr },
+ "Type inherits from native type '" + script_binding.type_name + "', so it can't be instantiated in object of type: '" + p_unmanaged->get_class() + "'.");
+
+ GCHandleIntPtr strong_gchandle =
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding(
+ &script_binding.type_name, p_unmanaged);
+
+ ERR_FAIL_NULL_V(strong_gchandle.value, { nullptr });
+
+ gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
+ script_binding.inited = true;
+
+ // Tie managed to unmanaged
+ RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
+
+ if (rc) {
+ // 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)
+ rc->reference();
+ CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
+ }
+
+ return gchandle.get_intptr();
+}
+
+void godotsharp_internal_tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) {
+ CSharpLanguage::tie_native_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_native_name, p_ref_counted);
+}
+
+void godotsharp_internal_tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, Ref<CSharpScript> *p_script, bool p_ref_counted) {
+ CSharpLanguage::tie_user_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_script, p_ref_counted);
+}
+
+void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
+ CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(p_gchandle_intptr, p_unmanaged);
+}
+
+void godotsharp_internal_new_csharp_script(Ref<CSharpScript> *r_dest) {
+ memnew_placement(r_dest, Ref<CSharpScript>(memnew(CSharpScript)));
+}
+
+bool godotsharp_internal_script_load(const String *p_path, Ref<CSharpScript> *r_dest) {
+ Ref<Resource> res = ResourceLoader::load(*p_path);
+ if (res.is_valid()) {
+ memnew_placement(r_dest, Ref<CSharpScript>(res));
+ return true;
+ } else {
+ memnew_placement(r_dest, Ref<CSharpScript>());
+ return false;
+ }
+}
+
+void godotsharp_internal_reload_registered_script(CSharpScript *p_script) {
+ CRASH_COND(!p_script);
+ CSharpScript::reload_registered_script(Ref<CSharpScript>(p_script));
+}
+
+void godotsharp_array_filter_godot_objects_by_native(StringName *p_native_name, const Array *p_input, Array *r_output) {
+ memnew_placement(r_output, Array);
+
+ for (int i = 0; i < p_input->size(); ++i) {
+ if (ClassDB::is_parent_class(((Object *)(*p_input)[i])->get_class(), *p_native_name)) {
+ r_output->push_back(p_input[i]);
+ }
+ }
+}
+
+void godotsharp_array_filter_godot_objects_by_non_native(const Array *p_input, Array *r_output) {
+ memnew_placement(r_output, Array);
+
+ for (int i = 0; i < p_input->size(); ++i) {
+ CSharpInstance *si = CAST_CSHARP_INSTANCE(((Object *)(*p_input)[i])->get_script_instance());
+
+ if (si != nullptr) {
+ r_output->push_back(p_input[i]);
+ }
+ }
+}
+
+void godotsharp_ref_new_from_ref_counted_ptr(Ref<RefCounted> *r_dest, RefCounted *p_ref_counted_ptr) {
+ memnew_placement(r_dest, Ref<RefCounted>(p_ref_counted_ptr));
+}
+
+void godotsharp_ref_destroy(Ref<RefCounted> *p_instance) {
+ p_instance->~Ref();
+}
+
+void godotsharp_string_name_new_from_string(StringName *r_dest, const String *p_name) {
+ memnew_placement(r_dest, StringName(*p_name));
+}
+
+void godotsharp_node_path_new_from_string(NodePath *r_dest, const String *p_name) {
+ memnew_placement(r_dest, NodePath(*p_name));
+}
+
+void godotsharp_string_name_as_string(String *r_dest, const StringName *p_name) {
+ memnew_placement(r_dest, String(p_name->operator String()));
+}
+
+void godotsharp_node_path_as_string(String *r_dest, const NodePath *p_np) {
+ memnew_placement(r_dest, String(p_np->operator String()));
+}
+
+godot_packed_array godotsharp_packed_byte_array_new_mem_copy(const uint8_t *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedByteArray);
+ PackedByteArray *array = reinterpret_cast<PackedByteArray *>(&ret);
+ array->resize(p_length);
+ uint8_t *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(uint8_t));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_int32_array_new_mem_copy(const int32_t *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedInt32Array);
+ PackedInt32Array *array = reinterpret_cast<PackedInt32Array *>(&ret);
+ array->resize(p_length);
+ int32_t *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(int32_t));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_int64_array_new_mem_copy(const int64_t *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedInt64Array);
+ PackedInt64Array *array = reinterpret_cast<PackedInt64Array *>(&ret);
+ array->resize(p_length);
+ int64_t *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(int64_t));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_float32_array_new_mem_copy(const float *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedFloat32Array);
+ PackedFloat32Array *array = reinterpret_cast<PackedFloat32Array *>(&ret);
+ array->resize(p_length);
+ float *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(float));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_float64_array_new_mem_copy(const double *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedFloat64Array);
+ PackedFloat64Array *array = reinterpret_cast<PackedFloat64Array *>(&ret);
+ array->resize(p_length);
+ double *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(double));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_vector2_array_new_mem_copy(const Vector2 *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedVector2Array);
+ PackedVector2Array *array = reinterpret_cast<PackedVector2Array *>(&ret);
+ array->resize(p_length);
+ Vector2 *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(Vector2));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_vector3_array_new_mem_copy(const Vector3 *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedVector3Array);
+ PackedVector3Array *array = reinterpret_cast<PackedVector3Array *>(&ret);
+ array->resize(p_length);
+ Vector3 *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(Vector3));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_color_array_new_mem_copy(const Color *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedColorArray);
+ PackedColorArray *array = reinterpret_cast<PackedColorArray *>(&ret);
+ array->resize(p_length);
+ Color *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(Color));
+ return ret;
+}
+
+void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String *p_element) {
+ r_dest->append(*p_element);
+}
+
+void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) {
+ // TODO: Use pooling for ManagedCallable instances.
+ CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle));
+ memnew_placement(r_callable, Callable(managed_callable));
+}
+
+bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable,
+ GCHandleIntPtr *r_delegate_handle, Object **r_object, StringName *r_name) {
+ if (p_callable->is_custom()) {
+ CallableCustom *custom = p_callable->get_custom();
+ CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
+
+ if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
+ ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
+ *r_delegate_handle = managed_callable->get_delegate();
+ *r_object = nullptr;
+ memnew_placement(r_name, StringName());
+ return true;
+ } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
+ SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
+ *r_delegate_handle = { nullptr };
+ *r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object());
+ 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<EventSignalCallable *>(custom);
+ *r_delegate_handle = { nullptr };
+ *r_object = ObjectDB::get_instance(event_signal_callable->get_object());
+ 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;
+ memnew_placement(r_name, StringName());
+ return false;
+ } else {
+ *r_delegate_handle = { nullptr };
+ *r_object = ObjectDB::get_instance(p_callable->get_object_id());
+ memnew_placement(r_name, StringName(p_callable->get_method()));
+ return true;
+ }
+}
+
+godot_variant godotsharp_callable_call(Callable *p_callable, const Variant **p_args, const int32_t p_arg_count, Callable::CallError *p_call_error) {
+ godot_variant ret;
+ memnew_placement(&ret, Variant);
+
+ Variant *ret_val = (Variant *)&ret;
+
+ p_callable->callp(p_args, p_arg_count, *ret_val, *p_call_error);
+
+ return ret;
+}
+
+void godotsharp_callable_call_deferred(Callable *p_callable, const Variant **p_args, const int32_t p_arg_count) {
+ p_callable->call_deferredp(p_args, p_arg_count);
+}
+
+// GDNative functions
+
+// gdnative.h
+
+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);
+}
+
+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());
+
+ 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
+
+void godotsharp_variant_new_copy(godot_variant *r_dest, const Variant *p_src) {
+ memnew_placement(r_dest, Variant(*p_src));
+}
+
+void godotsharp_variant_new_string_name(godot_variant *r_dest, const StringName *p_s) {
+ memnew_placement(r_dest, Variant(*p_s));
+}
+
+void godotsharp_variant_new_node_path(godot_variant *r_dest, const NodePath *p_np) {
+ memnew_placement(r_dest, Variant(*p_np));
+}
+
+void godotsharp_variant_new_object(godot_variant *r_dest, const Object *p_obj) {
+ memnew_placement(r_dest, Variant(p_obj));
+}
+
+void godotsharp_variant_new_transform2d(godot_variant *r_dest, const Transform2D *p_t2d) {
+ memnew_placement(r_dest, Variant(*p_t2d));
+}
+
+void godotsharp_variant_new_vector4(godot_variant *r_dest, const Vector4 *p_vec4) {
+ memnew_placement(r_dest, Variant(*p_vec4));
+}
+
+void godotsharp_variant_new_vector4i(godot_variant *r_dest, const Vector4i *p_vec4i) {
+ memnew_placement(r_dest, Variant(*p_vec4i));
+}
+
+void godotsharp_variant_new_basis(godot_variant *r_dest, const Basis *p_basis) {
+ memnew_placement(r_dest, Variant(*p_basis));
+}
+
+void godotsharp_variant_new_transform3d(godot_variant *r_dest, const Transform3D *p_trans) {
+ memnew_placement(r_dest, Variant(*p_trans));
+}
+
+void godotsharp_variant_new_projection(godot_variant *r_dest, const Projection *p_proj) {
+ memnew_placement(r_dest, Variant(*p_proj));
+}
+
+void godotsharp_variant_new_aabb(godot_variant *r_dest, const AABB *p_aabb) {
+ memnew_placement(r_dest, Variant(*p_aabb));
+}
+
+void godotsharp_variant_new_dictionary(godot_variant *r_dest, const Dictionary *p_dict) {
+ memnew_placement(r_dest, Variant(*p_dict));
+}
+
+void godotsharp_variant_new_array(godot_variant *r_dest, const Array *p_arr) {
+ memnew_placement(r_dest, Variant(*p_arr));
+}
+
+void godotsharp_variant_new_packed_byte_array(godot_variant *r_dest, const PackedByteArray *p_pba) {
+ memnew_placement(r_dest, Variant(*p_pba));
+}
+
+void godotsharp_variant_new_packed_int32_array(godot_variant *r_dest, const PackedInt32Array *p_pia) {
+ memnew_placement(r_dest, Variant(*p_pia));
+}
+
+void godotsharp_variant_new_packed_int64_array(godot_variant *r_dest, const PackedInt64Array *p_pia) {
+ memnew_placement(r_dest, Variant(*p_pia));
+}
+
+void godotsharp_variant_new_packed_float32_array(godot_variant *r_dest, const PackedFloat32Array *p_pra) {
+ memnew_placement(r_dest, Variant(*p_pra));
+}
+
+void godotsharp_variant_new_packed_float64_array(godot_variant *r_dest, const PackedFloat64Array *p_pra) {
+ memnew_placement(r_dest, Variant(*p_pra));
+}
+
+void godotsharp_variant_new_packed_string_array(godot_variant *r_dest, const PackedStringArray *p_psa) {
+ memnew_placement(r_dest, Variant(*p_psa));
+}
+
+void godotsharp_variant_new_packed_vector2_array(godot_variant *r_dest, const PackedVector2Array *p_pv2a) {
+ memnew_placement(r_dest, Variant(*p_pv2a));
+}
+
+void godotsharp_variant_new_packed_vector3_array(godot_variant *r_dest, const PackedVector3Array *p_pv3a) {
+ memnew_placement(r_dest, Variant(*p_pv3a));
+}
+
+void godotsharp_variant_new_packed_color_array(godot_variant *r_dest, const PackedColorArray *p_pca) {
+ memnew_placement(r_dest, Variant(*p_pca));
+}
+
+bool godotsharp_variant_as_bool(const Variant *p_self) {
+ return p_self->operator bool();
+}
+
+int64_t godotsharp_variant_as_int(const Variant *p_self) {
+ return p_self->operator int64_t();
+}
+
+double godotsharp_variant_as_float(const Variant *p_self) {
+ return p_self->operator double();
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+bool godotsharp_variant_equals(const godot_variant *p_a, const godot_variant *p_b) {
+ return *reinterpret_cast<const Variant *>(p_a) == *reinterpret_cast<const Variant *>(p_b);
+}
+
+// string.h
+
+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
+
+void godotsharp_string_name_new_copy(StringName *r_dest, const StringName *p_src) {
+ memnew_placement(r_dest, StringName(*p_src));
+}
+
+// node_path.h
+
+void godotsharp_node_path_new_copy(NodePath *r_dest, const NodePath *p_src) {
+ memnew_placement(r_dest, NodePath(*p_src));
+}
+
+// array.h
+
+void godotsharp_array_new(Array *r_dest) {
+ memnew_placement(r_dest, Array);
+}
+
+void godotsharp_array_new_copy(Array *r_dest, const Array *p_src) {
+ memnew_placement(r_dest, Array(*p_src));
+}
+
+godot_variant *godotsharp_array_ptrw(godot_array *p_self) {
+ return reinterpret_cast<godot_variant *>(&reinterpret_cast<Array *>(p_self)->operator[](0));
+}
+
+// dictionary.h
+
+void godotsharp_dictionary_new(Dictionary *r_dest) {
+ memnew_placement(r_dest, Dictionary);
+}
+
+void godotsharp_dictionary_new_copy(Dictionary *r_dest, const Dictionary *p_src) {
+ memnew_placement(r_dest, Dictionary(*p_src));
+}
+
+// destroy functions
+
+void godotsharp_packed_byte_array_destroy(PackedByteArray *p_self) {
+ p_self->~PackedByteArray();
+}
+
+void godotsharp_packed_int32_array_destroy(PackedInt32Array *p_self) {
+ p_self->~PackedInt32Array();
+}
+
+void godotsharp_packed_int64_array_destroy(PackedInt64Array *p_self) {
+ p_self->~PackedInt64Array();
+}
+
+void godotsharp_packed_float32_array_destroy(PackedFloat32Array *p_self) {
+ p_self->~PackedFloat32Array();
+}
+
+void godotsharp_packed_float64_array_destroy(PackedFloat64Array *p_self) {
+ p_self->~PackedFloat64Array();
+}
+
+void godotsharp_packed_string_array_destroy(PackedStringArray *p_self) {
+ p_self->~PackedStringArray();
+}
+
+void godotsharp_packed_vector2_array_destroy(PackedVector2Array *p_self) {
+ p_self->~PackedVector2Array();
+}
+
+void godotsharp_packed_vector3_array_destroy(PackedVector3Array *p_self) {
+ p_self->~PackedVector3Array();
+}
+
+void godotsharp_packed_color_array_destroy(PackedColorArray *p_self) {
+ p_self->~PackedColorArray();
+}
+
+void godotsharp_variant_destroy(Variant *p_self) {
+ p_self->~Variant();
+}
+
+void godotsharp_string_destroy(String *p_self) {
+ p_self->~String();
+}
+
+void godotsharp_string_name_destroy(StringName *p_self) {
+ p_self->~StringName();
+}
+
+void godotsharp_node_path_destroy(NodePath *p_self) {
+ p_self->~NodePath();
+}
+
+void godotsharp_signal_destroy(Signal *p_self) {
+ p_self->~Signal();
+}
+
+void godotsharp_callable_destroy(Callable *p_self) {
+ p_self->~Callable();
+}
+
+void godotsharp_array_destroy(Array *p_self) {
+ p_self->~Array();
+}
+
+void godotsharp_dictionary_destroy(Dictionary *p_self) {
+ p_self->~Dictionary();
+}
+
+// Array
+
+int32_t godotsharp_array_add(Array *p_self, const Variant *p_item) {
+ p_self->append(*p_item);
+ return p_self->size();
+}
+
+void godotsharp_array_duplicate(const Array *p_self, bool p_deep, Array *r_dest) {
+ memnew_placement(r_dest, Array(p_self->duplicate(p_deep)));
+}
+
+int32_t godotsharp_array_index_of(const Array *p_self, const Variant *p_item) {
+ return p_self->find(*p_item);
+}
+
+void godotsharp_array_insert(Array *p_self, int32_t p_index, const Variant *p_item) {
+ p_self->insert(p_index, *p_item);
+}
+
+void godotsharp_array_remove_at(Array *p_self, int32_t p_index) {
+ p_self->remove_at(p_index);
+}
+
+int32_t godotsharp_array_resize(Array *p_self, int32_t p_new_size) {
+ return (int32_t)p_self->resize(p_new_size);
+}
+
+void godotsharp_array_shuffle(Array *p_self) {
+ p_self->shuffle();
+}
+
+void godotsharp_array_to_string(const Array *p_self, String *r_str) {
+ *r_str = Variant(*p_self).operator String();
+}
+
+// Dictionary
+
+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;
+}
+
+void godotsharp_dictionary_set_value(Dictionary *p_self, const Variant *p_key, const Variant *p_value) {
+ p_self->operator[](*p_key) = *p_value;
+}
+
+void godotsharp_dictionary_keys(const Dictionary *p_self, Array *r_dest) {
+ memnew_placement(r_dest, Array(p_self->keys()));
+}
+
+void godotsharp_dictionary_values(const Dictionary *p_self, Array *r_dest) {
+ memnew_placement(r_dest, Array(p_self->values()));
+}
+
+int32_t godotsharp_dictionary_count(const Dictionary *p_self) {
+ return p_self->size();
+}
+
+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)));
+}
+
+void godotsharp_dictionary_add(Dictionary *p_self, const Variant *p_key, const Variant *p_value) {
+ p_self->operator[](*p_key) = *p_value;
+}
+
+void godotsharp_dictionary_clear(Dictionary *p_self) {
+ p_self->clear();
+}
+
+bool godotsharp_dictionary_contains_key(const Dictionary *p_self, const Variant *p_key) {
+ return p_self->has(*p_key);
+}
+
+void godotsharp_dictionary_duplicate(const Dictionary *p_self, bool p_deep, Dictionary *r_dest) {
+ memnew_placement(r_dest, Dictionary(p_self->duplicate(p_deep)));
+}
+
+bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) {
+ return p_self->erase(*p_key);
+}
+
+void godotsharp_dictionary_to_string(const Dictionary *p_self, String *r_str) {
+ *r_str = Variant(*p_self).operator String();
+}
+
+void godotsharp_string_md5_buffer(const String *p_self, PackedByteArray *r_md5_buffer) {
+ memnew_placement(r_md5_buffer, PackedByteArray(p_self->md5_buffer()));
+}
+
+void godotsharp_string_md5_text(const String *p_self, String *r_md5_text) {
+ memnew_placement(r_md5_text, String(p_self->md5_text()));
+}
+
+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);
+}
+
+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);
+}
+
+void godotsharp_string_sha256_buffer(const String *p_self, PackedByteArray *r_sha256_buffer) {
+ memnew_placement(r_sha256_buffer, PackedByteArray(p_self->sha256_buffer()));
+}
+
+void godotsharp_string_sha256_text(const String *p_self, String *r_sha256_text) {
+ memnew_placement(r_sha256_text, String(p_self->sha256_text()));
+}
+
+void godotsharp_string_simplify_path(const String *p_self, String *r_simplified_path) {
+ memnew_placement(r_simplified_path, String(p_self->simplify_path()));
+}
+
+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()));
+}
+
+void godotsharp_node_path_get_concatenated_names(const NodePath *p_self, String *r_subnames) {
+ memnew_placement(r_subnames, String(p_self->get_concatenated_names()));
+}
+
+void godotsharp_node_path_get_concatenated_subnames(const NodePath *p_self, String *r_subnames) {
+ memnew_placement(r_subnames, String(p_self->get_concatenated_subnames()));
+}
+
+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)));
+}
+
+int32_t godotsharp_node_path_get_name_count(const NodePath *p_self) {
+ return p_self->get_name_count();
+}
+
+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)));
+}
+
+int32_t godotsharp_node_path_get_subname_count(const NodePath *p_self) {
+ return p_self->get_subname_count();
+}
+
+bool godotsharp_node_path_is_absolute(const NodePath *p_self) {
+ return p_self->is_absolute();
+}
+
+void godotsharp_randomize() {
+ Math::randomize();
+}
+
+uint32_t godotsharp_randi() {
+ return Math::rand();
+}
+
+float godotsharp_randf() {
+ return Math::randf();
+}
+
+int32_t godotsharp_randi_range(int32_t p_from, int32_t p_to) {
+ return Math::random(p_from, p_to);
+}
+
+double godotsharp_randf_range(double p_from, double p_to) {
+ return Math::random(p_from, p_to);
+}
+
+double godotsharp_randfn(double p_mean, double p_deviation) {
+ return Math::randfn(p_mean, p_deviation);
+}
+
+void godotsharp_seed(uint64_t p_seed) {
+ Math::seed(p_seed);
+}
+
+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;
+}
+
+void godotsharp_weakref(Object *p_ptr, Ref<RefCounted> *r_weak_ref) {
+ if (!p_ptr) {
+ return;
+ }
+
+ Ref<WeakRef> wref;
+ RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
+
+ if (rc) {
+ Ref<RefCounted> 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<RefCounted>(wref));
+}
+
+void godotsharp_str(const godot_array *p_what, godot_string *r_ret) {
+ String &str = *memnew_placement(r_ret, String);
+ const Array &what = *reinterpret_cast<const 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;
+ }
+ }
+}
+
+void godotsharp_print(const godot_string *p_what) {
+ print_line(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_print_rich(const godot_string *p_what) {
+ print_line_rich(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_printerr(const godot_string *p_what) {
+ print_error(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_printt(const godot_string *p_what) {
+ print_line(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_prints(const godot_string *p_what) {
+ print_line(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_printraw(const godot_string *p_what) {
+ OS::get_singleton()->print("%s", reinterpret_cast<const String *>(p_what)->utf8().get_data());
+}
+
+void godotsharp_pusherror(const godot_string *p_str) {
+ ERR_PRINT(*reinterpret_cast<const String *>(p_str));
+}
+
+void godotsharp_pushwarning(const godot_string *p_str) {
+ WARN_PRINT(*reinterpret_cast<const String *>(p_str));
+}
+
+void godotsharp_var2str(const godot_variant *p_var, godot_string *r_ret) {
+ const Variant &var = *reinterpret_cast<const Variant *>(p_var);
+ String &vars = *memnew_placement(r_ret, String);
+ VariantWriter::write_to_string(var, vars);
+}
+
+void godotsharp_str2var(const godot_string *p_str, godot_variant *r_ret) {
+ Variant ret;
+
+ VariantParser::StreamString ss;
+ ss.s = *reinterpret_cast<const String *>(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));
+}
+
+void godotsharp_var2bytes(const godot_variant *p_var, bool p_full_objects, godot_packed_array *r_bytes) {
+ const Variant &var = *reinterpret_cast<const Variant *>(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);
+}
+
+void godotsharp_bytes2var(const godot_packed_array *p_bytes, bool p_allow_objects, godot_variant *r_ret) {
+ const PackedByteArray *bytes = reinterpret_cast<const PackedByteArray *>(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));
+}
+
+int godotsharp_hash(const godot_variant *p_var) {
+ return reinterpret_cast<const Variant *>(p_var)->hash();
+}
+
+void godotsharp_convert(const godot_variant *p_what, int32_t p_type, godot_variant *r_ret) {
+ const Variant *args[1] = { reinterpret_cast<const Variant *>(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<const Variant *>(p_what)->get_type()) +
+ "' to '" + Variant::get_type_name(Variant::Type(p_type)) + "'.");
+ }
+ memnew_placement(r_ret, Variant(ret));
+}
+
+Object *godotsharp_instance_from_id(uint64_t p_instance_id) {
+ return ObjectDB::get_instance(ObjectID(p_instance_id));
+}
+
+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
+
+// The order in this array must match the declaration order of
+// the methods in 'GodotSharp/Core/NativeInterop/NativeFuncs.cs'.
+static const void *unmanaged_callbacks[]{
+ (void *)godotsharp_method_bind_get_method,
+ (void *)godotsharp_get_class_constructor,
+ (void *)godotsharp_engine_get_singleton,
+ (void *)godotsharp_stack_info_vector_resize,
+ (void *)godotsharp_stack_info_vector_destroy,
+ (void *)godotsharp_internal_script_debugger_send_error,
+ (void *)godotsharp_internal_script_debugger_is_active,
+ (void *)godotsharp_internal_object_get_associated_gchandle,
+ (void *)godotsharp_internal_object_disposed,
+ (void *)godotsharp_internal_refcounted_disposed,
+ (void *)godotsharp_internal_signal_awaiter_connect,
+ (void *)godotsharp_internal_tie_native_managed_to_unmanaged,
+ (void *)godotsharp_internal_tie_user_managed_to_unmanaged,
+ (void *)godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup,
+ (void *)godotsharp_internal_unmanaged_get_script_instance_managed,
+ (void *)godotsharp_internal_unmanaged_get_instance_binding_managed,
+ (void *)godotsharp_internal_unmanaged_instance_binding_create_managed,
+ (void *)godotsharp_internal_new_csharp_script,
+ (void *)godotsharp_internal_script_load,
+ (void *)godotsharp_internal_reload_registered_script,
+ (void *)godotsharp_array_filter_godot_objects_by_native,
+ (void *)godotsharp_array_filter_godot_objects_by_non_native,
+ (void *)godotsharp_ref_new_from_ref_counted_ptr,
+ (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_callable_call,
+ (void *)godotsharp_callable_call_deferred,
+ (void *)godotsharp_method_bind_ptrcall,
+ (void *)godotsharp_method_bind_call,
+ (void *)godotsharp_variant_new_string_name,
+ (void *)godotsharp_variant_new_copy,
+ (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_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,
+ (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,
+ (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_array_to_string,
+ (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_dictionary_to_string,
+ (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,
+ (void *)godotsharp_bytes2var,
+ (void *)godotsharp_convert,
+ (void *)godotsharp_hash,
+ (void *)godotsharp_instance_from_id,
+ (void *)godotsharp_print,
+ (void *)godotsharp_print_rich,
+ (void *)godotsharp_printerr,
+ (void *)godotsharp_printraw,
+ (void *)godotsharp_prints,
+ (void *)godotsharp_printt,
+ (void *)godotsharp_randf,
+ (void *)godotsharp_randi,
+ (void *)godotsharp_randomize,
+ (void *)godotsharp_randf_range,
+ (void *)godotsharp_randfn,
+ (void *)godotsharp_randi_range,
+ (void *)godotsharp_rand_from_seed,
+ (void *)godotsharp_seed,
+ (void *)godotsharp_weakref,
+ (void *)godotsharp_str,
+ (void *)godotsharp_str2var,
+ (void *)godotsharp_var2bytes,
+ (void *)godotsharp_var2str,
+ (void *)godotsharp_pusherror,
+ (void *)godotsharp_pushwarning,
+ (void *)godotsharp_object_to_string,
+};
+
+const void **godotsharp::get_runtime_interop_funcs(int32_t &r_size) {
+ r_size = sizeof(unmanaged_callbacks);
+ return unmanaged_callbacks;
+}
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/glue/runtime_interop.h
index bf21283080..9725ced593 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/glue/runtime_interop.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gd_mono_header.h */
+/* runtime_interop.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,25 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GD_MONO_HEADER_H
-#define GD_MONO_HEADER_H
+#ifndef RUNTIME_INTEROP_H
+#define RUNTIME_INTEROP_H
-#include <cstdint>
+#include "core/typedefs.h"
-#ifdef WIN32
-#define GD_MONO_STDCALL __stdcall
-#else
-#define GD_MONO_STDCALL
-#endif
+namespace godotsharp {
+const void **get_runtime_interop_funcs(int32_t &r_size);
+}
-class GDMonoAssembly;
-class GDMonoClass;
-class GDMonoField;
-class GDMonoMethod;
-class GDMonoProperty;
-
-class IMonoClassMember;
-
-#include "managed_type.h"
-
-#endif // GD_MONO_HEADER_H
+#endif // RUNTIME_INTEROP_H
diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp
deleted file mode 100644
index c60e7c4869..0000000000
--- a/modules/mono/glue/scene_tree_glue.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*************************************************************************/
-/* scene_tree_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/object/class_db.h"
-#include "core/string/string_name.h"
-#include "core/variant/array.h"
-#include "scene/main/node.h"
-#include "scene/main/scene_tree.h"
-
-#include "../csharp_script.h"
-#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) {
- List<Node *> nodes;
- Array ret;
-
- // Retrieve all the nodes in the group
- ptr->get_nodes_in_group(*group, &nodes);
-
- // No need to bother if the group is empty
- if (!nodes.is_empty()) {
- MonoType *elem_type = mono_reflection_type_get_type(refltype);
- MonoClass *mono_class = mono_class_from_mono_type(elem_type);
- GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class);
-
- if (klass == GDMonoUtils::get_class_native_base(klass)) {
- // If we're trying to get native objects, just check the inheritance list
- 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]);
- }
- }
- } else {
- // If we're trying to get csharpscript instances, get the mono object and compare the classes
- for (int i = 0; i < nodes.size(); ++i) {
- CSharpInstance *si = CAST_CSHARP_INSTANCE(nodes[i]->get_script_instance());
-
- if (si != nullptr) {
- MonoObject *obj = si->get_mono_object();
- if (obj != nullptr && mono_object_get_class(obj) == mono_class) {
- ret.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
deleted file mode 100644
index fc6b13ceb3..0000000000
--- a/modules/mono/glue/string_glue.cpp
+++ /dev/null
@@ -1,85 +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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#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<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer();
- // TODO Check possible Array/Vector<uint8_t> 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<uint8_t> 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);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp
deleted file mode 100644
index 46d15316ba..0000000000
--- a/modules/mono/glue/string_name_glue.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*************************************************************************/
-/* string_name_glue.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/string/string_name.h"
-#include "core/string/ustring.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-StringName *godot_icall_StringName_Ctor(MonoString *p_path) {
- return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path)));
-}
-
-void godot_icall_StringName_Dtor(StringName *p_ptr) {
- ERR_FAIL_NULL(p_ptr);
- memdelete(p_ptr);
-}
-
-MonoString *godot_icall_StringName_operator_String(StringName *p_np) {
- return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
-}
-
-MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
- return (MonoBoolean)(*p_ptr == StringName());
-}
-
-void godot_register_string_name_icalls() {
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", godot_icall_StringName_Ctor);
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", godot_icall_StringName_Dtor);
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", godot_icall_StringName_operator_String);
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", godot_icall_StringName_is_empty);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index e5f1abe8d7..a81a52e4b8 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -33,14 +33,10 @@
#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 API_SOLUTION_NAME "GodotSharp"
#define CORE_API_ASSEMBLY_NAME "GodotSharp"
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define TOOLS_ASM_NAME "GodotTools"
-#define TOOLS_PROJECT_EDITOR_ASM_NAME "GodotTools.ProjectEditor"
#define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index f17b24e399..71576c2f80 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -96,8 +96,6 @@ class _GodotSharpDirs {
public:
String res_data_dir;
String res_metadata_dir;
- String res_assemblies_base_dir;
- String res_assemblies_dir;
String res_config_dir;
String res_temp_dir;
String res_temp_assemblies_base_dir;
@@ -105,15 +103,14 @@ public:
String mono_user_dir;
String mono_logs_dir;
+ String api_assemblies_base_dir;
+ String api_assemblies_dir;
+
#ifdef TOOLS_ENABLED
String mono_solutions_dir;
String build_logs_dir;
- String sln_filepath;
- String csproj_filepath;
-
String data_editor_tools_dir;
- String data_editor_prebuilt_api_dir;
#else
// Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
// Only defined on export templates. Used when exporting assemblies outside of PCKs.
@@ -131,8 +128,6 @@ private:
_GodotSharpDirs() {
res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().plus_file("mono");
res_metadata_dir = res_data_dir.plus_file("metadata");
- res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
- res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
// TODO use paths from csproj
@@ -140,6 +135,8 @@ private:
res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin");
res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config());
+ api_assemblies_base_dir = res_data_dir.plus_file("assemblies");
+
#ifdef JAVASCRIPT_ENABLED
mono_user_dir = "user://";
#else
@@ -151,16 +148,7 @@ private:
mono_solutions_dir = mono_user_dir.plus_file("solutions");
build_logs_dir = mono_user_dir.plus_file("build_logs");
- String appname = ProjectSettings::get_singleton()->get("application/config/name");
- String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.is_empty()) {
- appname_safe = "UnnamedProject";
- }
-
String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
-
- sln_filepath = base_path.plus_file(appname_safe + ".sln");
- csproj_filepath = base_path.plus_file(appname_safe + ".csproj");
#endif
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
@@ -169,7 +157,7 @@ private:
String data_dir_root = exe_dir.plus_file("GodotSharp");
data_editor_tools_dir = data_dir_root.plus_file("Tools");
- data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api");
+ api_assemblies_base_dir = data_dir_root.plus_file("Api");
String data_mono_root_dir = data_dir_root.plus_file("Mono");
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
@@ -189,8 +177,8 @@ private:
data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools");
}
- if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
- data_editor_prebuilt_api_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
+ if (!DirAccess::exists(api_assemblies_base_dir)) {
+ api_assemblies_base_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
}
if (!DirAccess::exists(data_mono_root_dir)) {
@@ -234,6 +222,12 @@ private:
#endif
#endif
+
+#ifdef TOOLS_ENABLED
+ api_assemblies_dir = api_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
+#else
+ api_assemblies_dir = data_dir_root;
+#endif
}
public:
@@ -251,14 +245,6 @@ String get_res_metadata_dir() {
return _GodotSharpDirs::get_singleton().res_metadata_dir;
}
-String get_res_assemblies_base_dir() {
- return _GodotSharpDirs::get_singleton().res_assemblies_base_dir;
-}
-
-String get_res_assemblies_dir() {
- return _GodotSharpDirs::get_singleton().res_assemblies_dir;
-}
-
String get_res_config_dir() {
return _GodotSharpDirs::get_singleton().res_config_dir;
}
@@ -275,6 +261,14 @@ String get_res_temp_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
}
+String get_api_assemblies_dir() {
+ return _GodotSharpDirs::get_singleton().api_assemblies_dir;
+}
+
+String get_api_assemblies_base_dir() {
+ return _GodotSharpDirs::get_singleton().api_assemblies_base_dir;
+}
+
String get_mono_user_dir() {
return _GodotSharpDirs::get_singleton().mono_user_dir;
}
@@ -292,21 +286,9 @@ String get_build_logs_dir() {
return _GodotSharpDirs::get_singleton().build_logs_dir;
}
-String get_project_sln_path() {
- return _GodotSharpDirs::get_singleton().sln_filepath;
-}
-
-String get_project_csproj_path() {
- return _GodotSharpDirs::get_singleton().csproj_filepath;
-}
-
String get_data_editor_tools_dir() {
return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
}
-
-String get_data_editor_prebuilt_api_dir() {
- return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
-}
#else
String get_data_game_assemblies_dir() {
return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;
diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h
index da25e0778f..03e62ffd82 100644
--- a/modules/mono/godotsharp_dirs.h
+++ b/modules/mono/godotsharp_dirs.h
@@ -37,13 +37,14 @@ namespace GodotSharpDirs {
String get_res_data_dir();
String get_res_metadata_dir();
-String get_res_assemblies_base_dir();
-String get_res_assemblies_dir();
String get_res_config_dir();
String get_res_temp_dir();
String get_res_temp_assemblies_base_dir();
String get_res_temp_assemblies_dir();
+String get_api_assemblies_dir();
+String get_api_assemblies_base_dir();
+
String get_mono_user_dir();
String get_mono_logs_dir();
@@ -51,11 +52,7 @@ String get_mono_logs_dir();
String get_mono_solutions_dir();
String get_build_logs_dir();
-String get_project_sln_path();
-String get_project_csproj_path();
-
String get_data_editor_tools_dir();
-String get_data_editor_prebuilt_api_dir();
#else
String get_data_game_assemblies_dir();
#endif
diff --git a/modules/mono/interop_types.h b/modules/mono/interop_types.h
new file mode 100644
index 0000000000..6942a91559
--- /dev/null
+++ b/modules/mono/interop_types.h
@@ -0,0 +1,208 @@
+/*************************************************************************/
+/* interop_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef INTEROP_TYPES_H
+#define INTEROP_TYPES_H
+
+#include "core/math/math_defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+// This is taken from the old GDNative, which was removed.
+
+#define GODOT_VARIANT_SIZE (sizeof(real_t) * 4 + sizeof(int64_t))
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VARIANT_SIZE];
+} godot_variant;
+
+#define GODOT_ARRAY_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_ARRAY_SIZE];
+} godot_array;
+
+#define GODOT_DICTIONARY_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_DICTIONARY_SIZE];
+} godot_dictionary;
+
+#define GODOT_STRING_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_STRING_SIZE];
+} godot_string;
+
+#define GODOT_STRING_NAME_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_STRING_NAME_SIZE];
+} godot_string_name;
+
+#define GODOT_PACKED_ARRAY_SIZE (2 * sizeof(void *))
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_ARRAY_SIZE];
+} godot_packed_array;
+
+#define GODOT_VECTOR2_SIZE (sizeof(real_t) * 2)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR2_SIZE];
+} godot_vector2;
+
+#define GODOT_VECTOR2I_SIZE (sizeof(int32_t) * 2)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR2I_SIZE];
+} godot_vector2i;
+
+#define GODOT_RECT2_SIZE (sizeof(real_t) * 4)
+
+typedef struct godot_rect2 {
+ uint8_t _dont_touch_that[GODOT_RECT2_SIZE];
+} godot_rect2;
+
+#define GODOT_RECT2I_SIZE (sizeof(int32_t) * 4)
+
+typedef struct godot_rect2i {
+ uint8_t _dont_touch_that[GODOT_RECT2I_SIZE];
+} godot_rect2i;
+
+#define GODOT_VECTOR3_SIZE (sizeof(real_t) * 3)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR3_SIZE];
+} godot_vector3;
+
+#define GODOT_VECTOR3I_SIZE (sizeof(int32_t) * 3)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR3I_SIZE];
+} godot_vector3i;
+
+#define GODOT_TRANSFORM2D_SIZE (sizeof(real_t) * 6)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_TRANSFORM2D_SIZE];
+} godot_transform2d;
+
+#define GODOT_VECTOR4_SIZE (sizeof(real_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR4_SIZE];
+} godot_vector4;
+
+#define GODOT_VECTOR4I_SIZE (sizeof(int32_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR4I_SIZE];
+} godot_vector4i;
+
+#define GODOT_PLANE_SIZE (sizeof(real_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PLANE_SIZE];
+} godot_plane;
+
+#define GODOT_QUATERNION_SIZE (sizeof(real_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_QUATERNION_SIZE];
+} godot_quaternion;
+
+#define GODOT_AABB_SIZE (sizeof(real_t) * 6)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_AABB_SIZE];
+} godot_aabb;
+
+#define GODOT_BASIS_SIZE (sizeof(real_t) * 9)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_BASIS_SIZE];
+} godot_basis;
+
+#define GODOT_TRANSFORM3D_SIZE (sizeof(real_t) * 12)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_TRANSFORM3D_SIZE];
+} godot_transform3d;
+
+#define GODOT_PROJECTION_SIZE (sizeof(real_t) * 4 * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PROJECTION_SIZE];
+} godot_projection;
+
+// Colors should always use 32-bit floats, so don't use real_t here.
+#define GODOT_COLOR_SIZE (sizeof(float) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_COLOR_SIZE];
+} godot_color;
+
+#define GODOT_NODE_PATH_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_NODE_PATH_SIZE];
+} godot_node_path;
+
+#define GODOT_RID_SIZE sizeof(uint64_t)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_RID_SIZE];
+} godot_rid;
+
+// Alignment hardcoded in `core/variant/callable.h`.
+#define GODOT_CALLABLE_SIZE (16)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_CALLABLE_SIZE];
+} godot_callable;
+
+// Alignment hardcoded in `core/variant/callable.h`.
+#define GODOT_SIGNAL_SIZE (16)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_SIGNAL_SIZE];
+} godot_signal;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // INTEROP_TYPES_H
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index c159bb9eea..9305dc645a 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -31,8 +31,7 @@
#include "managed_callable.h"
#include "csharp_script.h"
-#include "mono_gd/gd_mono_marshal.h"
-#include "mono_gd/gd_mono_utils.h"
+#include "mono_gd/gd_mono_cache.h"
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable>::List ManagedCallable::instances;
@@ -44,18 +43,16 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus
const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a);
const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b);
- MonoDelegate *delegate_a = (MonoDelegate *)a->delegate_handle.get_target();
- MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle.get_target();
-
- if (!delegate_a || !delegate_b) {
- if (!delegate_a && !delegate_b) {
+ if (!a->delegate_handle.value || !b->delegate_handle.value) {
+ if (!a->delegate_handle.value && !b->delegate_handle.value) {
return true;
}
return false;
}
// Call Delegate's 'Equals'
- return GDMonoUtils::mono_delegate_equal(delegate_a, delegate_b);
+ return GDMonoCache::managed_callbacks.DelegateUtils_DelegateEquals(
+ a->delegate_handle, b->delegate_handle);
}
bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -66,8 +63,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.value);
}
String ManagedCallable::get_as_text() const {
@@ -91,41 +87,24 @@ 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();
+ ERR_FAIL_COND(delegate_handle.value == nullptr);
- MonoException *exc = nullptr;
- MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc);
+ GDMonoCache::managed_callbacks.DelegateUtils_InvokeWithVariantArgs(
+ delegate_handle, p_arguments, p_argcount, &r_return_value);
- 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;
- }
+ 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.value) {
+ GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(delegate_handle);
+ 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(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(instances_mutex);
@@ -133,6 +112,7 @@ ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) {
}
#endif
}
+/* clang-format on */
ManagedCallable::~ManagedCallable() {
#ifdef GD_MONO_HOT_RELOAD
@@ -143,5 +123,5 @@ ManagedCallable::~ManagedCallable() {
}
#endif
- delegate_handle.release();
+ release_delegate_handle();
}
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index 11bee6cf60..aa3344f4d5 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -31,19 +31,15 @@
#ifndef MANAGED_CALLABLE_H
#define MANAGED_CALLABLE_H
-#include <mono/metadata/object.h>
-
#include "core/os/mutex.h"
#include "core/templates/self_list.h"
#include "core/variant/callable.h"
#include "mono_gc_handle.h"
-#include "mono_gd/gd_mono_method.h"
class ManagedCallable : public CallableCustom {
friend class CSharpLanguage;
- MonoGCHandleData delegate_handle;
- GDMonoMethod *delegate_invoke = nullptr;
+ GCHandleIntPtr delegate_handle;
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
@@ -60,9 +56,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_ GCHandleIntPtr 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 +64,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(GCHandleIntPtr p_delegate_handle);
~ManagedCallable();
};
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index f3dafa6ecf..9cf0a641b9 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -31,34 +31,20 @@
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
+#include "mono_gd/gd_mono_cache.h"
void MonoGCHandleData::release() {
#ifdef DEBUG_ENABLED
- CRASH_COND(handle && GDMono::get_singleton() == nullptr);
+ CRASH_COND(handle.value && GDMono::get_singleton() == nullptr);
#endif
- if (handle && GDMono::get_singleton()->is_runtime_initialized()) {
- GDMonoUtils::free_gchandle(handle);
- handle = 0;
+ if (handle.value && GDMonoCache::godot_api_cache_updated &&
+ GDMono::get_singleton()->is_runtime_initialized()) {
+ free_gchandle(handle);
+ handle.value = nullptr;
}
}
-
-MonoGCHandleData MonoGCHandleData::new_strong_handle(MonoObject *p_object) {
- return MonoGCHandleData(GDMonoUtils::new_strong_gchandle(p_object), gdmono::GCHandleType::STRONG_HANDLE);
-}
-
-MonoGCHandleData MonoGCHandleData::new_strong_handle_pinned(MonoObject *p_object) {
- return MonoGCHandleData(GDMonoUtils::new_strong_gchandle_pinned(p_object), gdmono::GCHandleType::STRONG_HANDLE);
-}
-
-MonoGCHandleData MonoGCHandleData::new_weak_handle(MonoObject *p_object) {
- return MonoGCHandleData(GDMonoUtils::new_weak_gchandle(p_object), gdmono::GCHandleType::WEAK_HANDLE);
-}
-
-Ref<MonoGCHandleRef> MonoGCHandleRef::create_strong(MonoObject *p_object) {
- return memnew(MonoGCHandleRef(MonoGCHandleData::new_strong_handle(p_object)));
-}
-
-Ref<MonoGCHandleRef> MonoGCHandleRef::create_weak(MonoObject *p_object) {
- return memnew(MonoGCHandleRef(MonoGCHandleData::new_weak_handle(p_object)));
+void MonoGCHandleData::free_gchandle(GCHandleIntPtr p_gchandle) {
+ CRASH_COND(!GDMonoCache::godot_api_cache_updated);
+ GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(p_gchandle);
}
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index e2aff1d19d..4e4c13fee6 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -31,8 +31,6 @@
#ifndef MONO_GC_HANDLE_H
#define MONO_GC_HANDLE_H
-#include <mono/jit/jit.h>
-
#include "core/object/ref_counted.h"
namespace gdmono {
@@ -44,18 +42,32 @@ enum class GCHandleType : char {
};
}
+extern "C" {
+struct GCHandleIntPtr {
+ void *value;
+
+ _FORCE_INLINE_ bool operator==(const GCHandleIntPtr &p_other) { return value == p_other.value; }
+ _FORCE_INLINE_ bool operator!=(const GCHandleIntPtr &p_other) { return value != p_other.value; }
+
+ GCHandleIntPtr() = delete;
+};
+}
+
+static_assert(sizeof(GCHandleIntPtr) == sizeof(void *));
+
// Manual release of the GC handle must be done when using this struct
struct MonoGCHandleData {
- uint32_t handle = 0;
+ GCHandleIntPtr handle = { nullptr };
gdmono::GCHandleType type = gdmono::GCHandleType::NIL;
- _FORCE_INLINE_ bool is_released() const { return !handle; }
+ _FORCE_INLINE_ bool is_released() const { return !handle.value; }
_FORCE_INLINE_ bool is_weak() const { return type == gdmono::GCHandleType::WEAK_HANDLE; }
-
- _FORCE_INLINE_ MonoObject *get_target() const { return handle ? mono_gchandle_get_target(handle) : nullptr; }
+ _FORCE_INLINE_ GCHandleIntPtr get_intptr() const { return handle; }
void release();
+ static void free_gchandle(GCHandleIntPtr p_gchandle);
+
void operator=(const MonoGCHandleData &p_other) {
#ifdef DEBUG_ENABLED
CRASH_COND(!is_released());
@@ -68,40 +80,10 @@ struct MonoGCHandleData {
MonoGCHandleData() {}
- MonoGCHandleData(uint32_t p_handle, gdmono::GCHandleType p_type) :
+ MonoGCHandleData(GCHandleIntPtr p_handle, gdmono::GCHandleType p_type) :
handle(p_handle),
type(p_type) {
}
-
- static MonoGCHandleData new_strong_handle(MonoObject *p_object);
- static MonoGCHandleData new_strong_handle_pinned(MonoObject *p_object);
- static MonoGCHandleData new_weak_handle(MonoObject *p_object);
-};
-
-class MonoGCHandleRef : public RefCounted {
- GDCLASS(MonoGCHandleRef, RefCounted);
-
- MonoGCHandleData data;
-
-public:
- static Ref<MonoGCHandleRef> create_strong(MonoObject *p_object);
- static Ref<MonoGCHandleRef> create_weak(MonoObject *p_object);
-
- _FORCE_INLINE_ bool is_released() const { return data.is_released(); }
- _FORCE_INLINE_ bool is_weak() const { return data.is_weak(); }
-
- _FORCE_INLINE_ MonoObject *get_target() const { return data.get_target(); }
-
- void release() { data.release(); }
-
- _FORCE_INLINE_ void set_handle(uint32_t p_handle, gdmono::GCHandleType p_handle_type) {
- data = MonoGCHandleData(p_handle, p_handle_type);
- }
-
- MonoGCHandleRef(const MonoGCHandleData &p_gc_handle_data) :
- data(p_gc_handle_data) {
- }
- ~MonoGCHandleRef() { release(); }
};
#endif // MONO_GC_HANDLE_H
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index d3d3bb2bef..0532cc915b 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -30,13 +30,6 @@
#include "gd_mono.h"
-#include <mono/metadata/environment.h>
-#include <mono/metadata/exception.h>
-#include <mono/metadata/mono-config.h>
-#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/mono-gc.h>
-#include <mono/metadata/profiler.h>
-
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/io/dir_access.h"
@@ -45,1077 +38,519 @@
#include "core/os/thread.h"
#include "../csharp_script.h"
+#include "../glue/runtime_interop.h"
#include "../godotsharp_dirs.h"
#include "../utils/path_utils.h"
#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
+#ifdef TOOLS_ENABLED
+#include <nethost.h>
+#endif
+
+#include <coreclr_delegates.h>
+#include <hostfxr.h>
+#ifdef UNIX_ENABLED
+#include <dlfcn.h>
+#endif
+
+// TODO mobile
+#if 0
#ifdef ANDROID_ENABLED
-#include "android_mono_config.h"
#include "support/android_support.h"
#elif defined(IOS_ENABLED)
#include "support/ios_support.h"
#endif
-
-#if defined(TOOL_ENABLED) && defined(GD_MONO_SINGLE_APPDOMAIN)
-// This will no longer be the case if we replace appdomains with AssemblyLoadContext
-#error "Editor build requires support for multiple appdomains"
-#endif
-
-#if defined(GD_MONO_HOT_RELOAD) && defined(GD_MONO_SINGLE_APPDOMAIN)
-#error "Hot reloading requires multiple appdomains"
#endif
-// TODO:
-// This has turned into a gigantic mess. There's too much going on here. Too much #ifdef as well.
-// It's just painful to read... It needs to be re-structured. Please, clean this up, future me.
-
GDMono *GDMono::singleton = nullptr;
namespace {
-
-#if defined(JAVASCRIPT_ENABLED)
-extern "C" {
-void mono_wasm_load_runtime(const char *managed_path, int enable_debugging);
-}
-#endif
-
-#if !defined(JAVASCRIPT_ENABLED)
-
-void gd_mono_setup_runtime_main_args() {
- CharString execpath = OS::get_singleton()->get_executable_path().utf8();
-
- List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
-
- List<CharString> cmdline_args_utf8;
- Vector<char *> main_args;
- main_args.resize(cmdline_args.size() + 1);
-
- main_args.write[0] = execpath.ptrw();
-
- int i = 1;
- for (const String &E : cmdline_args) {
- CharString &stored = cmdline_args_utf8.push_back(E.utf8())->get();
- main_args.write[i] = stored.ptrw();
- i++;
- }
-
- mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
-}
-
-void gd_mono_profiler_init() {
- String profiler_args = GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
- bool profiler_enabled = GLOBAL_DEF("mono/profiler/enabled", false);
- if (profiler_enabled) {
- mono_profiler_load(profiler_args.utf8());
- return;
- }
-
- const String env_var_name = "MONO_ENV_OPTIONS";
- if (OS::get_singleton()->has_environment(env_var_name)) {
- const String mono_env_ops = OS::get_singleton()->get_environment(env_var_name);
- // Usually MONO_ENV_OPTIONS looks like: --profile=jb:prof=timeline,ctl=remote,host=127.0.0.1:55467
- const String prefix = "--profile=";
- if (mono_env_ops.begins_with(prefix)) {
- const String ops = mono_env_ops.substr(prefix.length(), mono_env_ops.length());
- mono_profiler_load(ops.utf8());
- }
- }
-}
-
-void gd_mono_debug_init() {
- CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
-
- if (da_args.length()) {
- OS::get_singleton()->set_environment("GODOT_MONO_DEBUGGER_AGENT", String());
- }
-
-#ifdef TOOLS_ENABLED
- int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
- bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
- int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
-
- if (Engine::get_singleton()->is_editor_hint() ||
- ProjectSettings::get_singleton()->get_resource_path().is_empty() ||
- Engine::get_singleton()->is_project_manager_hint()) {
- if (da_args.size() == 0) {
- return;
- }
- }
-
- if (da_args.length() == 0) {
- da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
- ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
- .utf8();
- }
+hostfxr_initialize_for_dotnet_command_line_fn hostfxr_initialize_for_dotnet_command_line = nullptr;
+hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr;
+hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr;
+hostfxr_close_fn hostfxr_close = nullptr;
+
+#ifdef _WIN32
+static_assert(sizeof(char_t) == sizeof(char16_t));
+using HostFxrCharString = Char16String;
+#define HOSTFXR_STR(m_str) L##m_str
#else
- if (da_args.length() == 0) {
- return; // Exported games don't use the project settings to setup the debugger agent
- }
+static_assert(sizeof(char_t) == sizeof(char));
+using HostFxrCharString = CharString;
+#define HOSTFXR_STR(m_str) m_str
#endif
- // Debugging enabled
-
- mono_debug_init(MONO_DEBUG_FORMAT_MONO);
-
- // --debugger-agent=help
- const char *options[] = {
- "--soft-breakpoints",
- da_args.get_data()
- };
- mono_jit_parse_options(2, (char **)options);
-}
-
-#endif // !defined(JAVASCRIPT_ENABLED)
-
-#if defined(JAVASCRIPT_ENABLED)
-MonoDomain *gd_initialize_mono_runtime() {
- const char *vfs_prefix = "managed";
- int enable_debugging = 0;
-
- // TODO: Provide a way to enable debugging on WASM release builds.
-#ifdef DEBUG_ENABLED
- enable_debugging = 1;
+HostFxrCharString str_to_hostfxr(const String &p_str) {
+#ifdef _WIN32
+ return p_str.utf16();
+#else
+ return p_str.utf8();
#endif
-
- mono_wasm_load_runtime(vfs_prefix, enable_debugging);
-
- return mono_get_root_domain();
}
-#else
-MonoDomain *gd_initialize_mono_runtime() {
- gd_mono_debug_init();
-#if defined(IOS_ENABLED) || defined(ANDROID_ENABLED)
- // I don't know whether this actually matters or not
- const char *runtime_version = "mobile";
+#ifdef TOOLS_ENABLED
+String str_from_hostfxr(const char_t *p_buffer) {
+#ifdef _WIN32
+ return String::utf16((const char16_t *)p_buffer);
#else
- const char *runtime_version = "v4.0.30319";
+ return String::utf8((const char *)p_buffer);
#endif
-
- return mono_jit_init_version("GodotEngine.RootDomain", runtime_version);
}
#endif
-} // namespace
-
-void GDMono::add_mono_shared_libs_dir_to_path() {
- // TODO: Replace this with a mono_dl_fallback
-
- // By default Mono seems to search shared libraries in the following directories:
- // Current working directory, @executable_path@ and PATH
- // The parent directory of the image file (assembly where the dllimport method is declared)
- // @executable_path@/../lib
- // @executable_path@/../Libraries (__MACH__ only)
- // This does not work when embedding Mono unless we use the same directory structure.
- // To fix this we append the directory containing our shared libraries to PATH.
-
-#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED)
- String path_var("PATH");
- String path_value = OS::get_singleton()->get_environment(path_var);
-
-#ifdef WINDOWS_ENABLED
- path_value += ';';
+const char_t *get_data(const HostFxrCharString &p_char_str) {
+ return (const char_t *)p_char_str.get_data();
+}
- String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir();
#ifdef TOOLS_ENABLED
- if (DirAccess::exists(bundled_bin_dir)) {
- path_value += bundled_bin_dir;
- } else {
- path_value += mono_reg_info.bin_dir;
- }
-#else
- if (DirAccess::exists(bundled_bin_dir)) {
- path_value += bundled_bin_dir;
- }
-#endif // TOOLS_ENABLED
+String find_hostfxr(size_t p_known_buffet_size, get_hostfxr_parameters *p_get_hostfxr_params) {
+ // Pre-allocate a large buffer for the path to hostfxr
+ Vector<char_t> buffer;
+ buffer.resize(p_known_buffet_size);
-#else
- path_value += ':';
+ int rc = get_hostfxr_path(buffer.ptrw(), &p_known_buffet_size, p_get_hostfxr_params);
- String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir();
- if (DirAccess::exists(bundled_lib_dir)) {
- path_value += bundled_lib_dir;
- } else {
- // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms?
- }
-#endif // WINDOWS_ENABLED
+ ERR_FAIL_COND_V_MSG(rc != 0, String(), "get_hostfxr_path failed with code: " + itos(rc));
- OS::get_singleton()->set_environment(path_var, path_value);
-#endif // WINDOWS_ENABLED || UNIX_ENABLED
+ return str_from_hostfxr(buffer.ptr());
}
+#endif
-void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir) {
- String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
- String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
-
+String find_hostfxr() {
#ifdef TOOLS_ENABLED
-
-#if defined(WINDOWS_ENABLED)
- mono_reg_info = MonoRegUtils::find_mono();
-
- if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
- r_assembly_rootdir = mono_reg_info.assembly_dir;
- }
-
- if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
- r_config_dir = mono_reg_info.config_dir;
- }
-#elif defined(MACOS_ENABLED)
- const char *c_assembly_rootdir = mono_assembly_getrootdir();
- const char *c_config_dir = mono_get_config_dir();
-
- if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) {
- Vector<const char *> locations;
- locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
- locations.push_back("/usr/local/var/homebrew/linked/mono/");
-
- for (int i = 0; i < locations.size(); i++) {
- String hint_assembly_rootdir = path::join(locations[i], "lib");
- String hint_mscorlib_path = path::join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
- String hint_config_dir = path::join(locations[i], "etc");
-
- if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
- r_assembly_rootdir = hint_assembly_rootdir;
- r_config_dir = hint_config_dir;
- break;
+ const int CoreHostLibMissingFailure = 0x80008083;
+ const int HostApiBufferTooSmall = 0x80008098;
+
+ size_t buffer_size = 0;
+ int rc = get_hostfxr_path(nullptr, &buffer_size, nullptr);
+
+ if (rc == HostApiBufferTooSmall) {
+ return find_hostfxr(buffer_size, nullptr);
+ }
+
+ if (rc == CoreHostLibMissingFailure) {
+ // Apparently `get_hostfxr_path` doesn't look for dotnet in `PATH`? (I suppose it needs the
+ // `DOTNET_ROOT` environment variable). If it fails, we try to find the dotnet executable
+ // in `PATH` ourselves and pass its location as `dotnet_root` to `get_hostfxr_path`.
+ String dotnet_exe = path::find_executable("dotnet");
+
+ if (!dotnet_exe.is_empty()) {
+ // The file found in PATH may be a symlink
+ dotnet_exe = path::abspath(path::realpath(dotnet_exe));
+
+ // TODO:
+ // Sometimes, the symlink may not point to the dotnet executable in the dotnet root.
+ // That's the case with snaps. The snap install should have been found with the
+ // previous `get_hostfxr_path`, but it would still be better to do this properly
+ // and use something like `dotnet --list-sdks/runtimes` to find the actual location.
+ // This way we could also check if the proper sdk or runtime is installed. This would
+ // allow us to fail gracefully and show some helpful information in the editor.
+
+ HostFxrCharString dotnet_root = str_to_hostfxr(dotnet_exe.get_base_dir());
+
+ get_hostfxr_parameters get_hostfxr_parameters = {
+ sizeof(get_hostfxr_parameters),
+ nullptr,
+ get_data(dotnet_root)
+ };
+
+ buffer_size = 0;
+ rc = get_hostfxr_path(nullptr, &buffer_size, &get_hostfxr_parameters);
+ if (rc == HostApiBufferTooSmall) {
+ return find_hostfxr(buffer_size, &get_hostfxr_parameters);
}
}
}
-#endif
- if (DirAccess::exists(bundled_assembly_rootdir)) {
- r_assembly_rootdir = bundled_assembly_rootdir;
+ if (rc == CoreHostLibMissingFailure) {
+ ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " +
+ "Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " +
+ "libraries are not present in the expected locations.");
}
- if (DirAccess::exists(bundled_config_dir)) {
- r_config_dir = bundled_config_dir;
- }
-
-#ifdef WINDOWS_ENABLED
- if (r_assembly_rootdir.is_empty() || r_config_dir.is_empty()) {
- ERR_PRINT("Cannot find Mono in the registry.");
- // Assertion: if they are not set, then they weren't found in the registry
- CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
- }
-#endif // WINDOWS_ENABLED
-
+ return String();
#else
- // Export templates always use the bundled directories
- r_assembly_rootdir = bundled_assembly_rootdir;
- r_config_dir = bundled_config_dir;
-#endif
-}
-
-void GDMono::initialize() {
- ERR_FAIL_NULL(Engine::get_singleton());
-
- print_verbose("Mono: Initializing module...");
-
- char *runtime_build_info = mono_get_runtime_build_info();
- print_verbose("Mono JIT compiler version " + String(runtime_build_info));
- mono_free(runtime_build_info);
-
- _init_godot_api_hashes();
- _init_exception_policy();
-
- GDMonoLog::get_singleton()->initialize();
-
-#if !defined(JAVASCRIPT_ENABLED)
- String assembly_rootdir;
- String config_dir;
- determine_mono_dirs(assembly_rootdir, config_dir);
-
- // Leak if we call mono_set_dirs more than once
- mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : nullptr,
- config_dir.length() ? config_dir.utf8().get_data() : nullptr);
- add_mono_shared_libs_dir_to_path();
-#endif
-
-#ifdef ANDROID_ENABLED
- mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
+#if defined(WINDOWS_ENABLED)
+ String probe_path = GodotSharpDirs::get_api_assemblies_dir()
+ .plus_file("hostfxr.dll");
+#elif defined(MACOS_ENABLED)
+ String probe_path = GodotSharpDirs::get_api_assemblies_dir()
+ .plus_file("libhostfxr.dylib");
+#elif defined(UNIX_ENABLED)
+ String probe_path = GodotSharpDirs::get_api_assemblies_dir()
+ .plus_file("libhostfxr.so");
#else
- mono_config_parse(nullptr);
+#error "Platform not supported (yet?)"
#endif
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::initialize();
-#elif defined(IOS_ENABLED)
- gdmono::ios::support::initialize();
-#endif
+ if (FileAccess::exists(probe_path)) {
+ return probe_path;
+ }
- GDMonoAssembly::initialize();
+ return String();
-#if !defined(JAVASCRIPT_ENABLED)
- gd_mono_profiler_init();
#endif
+}
- mono_install_unhandled_exception_hook(&unhandled_exception_hook, nullptr);
-
-#ifndef TOOLS_ENABLED
- // Exported games that don't use C# must still work. They likely don't ship with mscorlib.
- // We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash.
- if (GDMonoAssembly::find_assembly("mscorlib.dll").is_empty()) {
- print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found");
- return;
- }
-#endif
+bool load_hostfxr(void *&r_hostfxr_dll_handle) {
+ String hostfxr_path = find_hostfxr();
-#if !defined(NO_MONO_THREADS_SUSPEND_WORKAROUND)
- // FIXME: Temporary workaround. See: https://github.com/godotengine/godot/issues/29812
- if (!OS::get_singleton()->has_environment("MONO_THREADS_SUSPEND")) {
- OS::get_singleton()->set_environment("MONO_THREADS_SUSPEND", "preemptive");
+ if (hostfxr_path.is_empty()) {
+ return false;
}
-#endif
- // NOTE: Internal calls must be registered after the Mono runtime initialization.
- // Otherwise registration fails with the error: 'assertion 'hash != nullptr' failed'.
+ print_verbose("Found hostfxr: " + hostfxr_path);
- root_domain = gd_initialize_mono_runtime();
- ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
+ Error err = OS::get_singleton()->open_dynamic_library(hostfxr_path, r_hostfxr_dll_handle);
- GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
-
-#if !defined(JAVASCRIPT_ENABLED)
- gd_mono_setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
-#endif
+ if (err != OK) {
+ return false;
+ }
- runtime_initialized = true;
+ void *lib = r_hostfxr_dll_handle;
- print_verbose("Mono: Runtime initialized");
+ void *symbol = nullptr;
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::register_internal_calls();
-#endif
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_dotnet_command_line", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)symbol;
- // mscorlib assembly MUST be present at initialization
- bool corlib_loaded = _load_corlib_assembly();
- ERR_FAIL_COND_MSG(!corlib_loaded, "Mono: Failed to load mscorlib assembly.");
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_runtime_config", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)symbol;
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- Error domain_load_err = _load_scripts_domain();
- ERR_FAIL_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
-#else
- scripts_domain = root_domain;
-#endif
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_get_runtime_delegate", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)symbol;
- _register_internal_calls();
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_close", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_close = (hostfxr_close_fn)symbol;
- print_verbose("Mono: INITIALIZED");
+ return (hostfxr_initialize_for_runtime_config &&
+ hostfxr_get_runtime_delegate &&
+ hostfxr_close);
}
-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.
-
- _load_api_assemblies();
-
-#if defined(TOOLS_ENABLED)
- bool tool_assemblies_loaded = _load_tools_assemblies();
- CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
-
- if (Engine::get_singleton()->is_project_manager_hint()) {
- return;
- }
-#endif
-
- // Load the project's main assembly. This doesn't necessarily need to succeed.
- // The game may not be using .NET at all, or if the project does use .NET and
- // we're running in the editor, it may just happen to be it wasn't built yet.
- if (!_load_project_assembly()) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load project assembly");
- }
- }
-}
-
-bool GDMono::_are_api_assemblies_out_of_sync() {
- bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoCache::cached_data.godot_api_cache_updated);
#ifdef TOOLS_ENABLED
- if (!out_of_sync) {
- out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync;
+load_assembly_and_get_function_pointer_fn initialize_hostfxr_for_config(const char_t *p_config_path) {
+ hostfxr_handle cxt = nullptr;
+ int rc = hostfxr_initialize_for_runtime_config(p_config_path, nullptr, &cxt);
+ if (rc != 0 || cxt == nullptr) {
+ hostfxr_close(cxt);
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_runtime_config failed with code: " + itos(rc));
}
-#endif
- return out_of_sync;
-}
-namespace GodotSharpBindings {
-#ifdef MONO_GLUE_ENABLED
+ void *load_assembly_and_get_function_pointer = nullptr;
-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();
+ rc = hostfxr_get_runtime_delegate(cxt,
+ hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
+ }
-#else
+ hostfxr_close(cxt);
-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();
+ return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
+#else
+load_assembly_and_get_function_pointer_fn initialize_hostfxr_self_contained(
+ const char_t *p_main_assembly_path) {
+ hostfxr_handle cxt = nullptr;
-void register_generated_icalls() {
- /* Fine, just do nothing */
-}
+ List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
-#endif // MONO_GLUE_ENABLED
-} // namespace GodotSharpBindings
+ List<HostFxrCharString> argv_store;
+ Vector<const char_t *> argv;
+ argv.resize(cmdline_args.size() + 1);
-void GDMono::_register_internal_calls() {
- GodotSharpBindings::register_generated_icalls();
-}
+ argv.write[0] = p_main_assembly_path;
-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.");
+ int i = 1;
+ for (const String &E : cmdline_args) {
+ HostFxrCharString &stored = argv_store.push_back(str_to_hostfxr(E))->get();
+ argv.write[i] = stored.ptr();
+ i++;
}
-#ifdef TOOLS_ENABLED
- if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) {
- ERR_PRINT("Mono: Editor API hash mismatch.");
+ int rc = hostfxr_initialize_for_dotnet_command_line(argv.size(), argv.ptrw(), nullptr, &cxt);
+ if (rc != 0 || cxt == nullptr) {
+ hostfxr_close(cxt);
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_dotnet_command_line failed with code: " + itos(rc));
}
-#endif // TOOLS_ENABLED
-#endif // MONO_GLUE_ENABLED && DEBUG_METHODS_ENABLED
-}
-void GDMono::_init_exception_policy() {
- PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/runtime/unhandled_exception_policy", PROPERTY_HINT_ENUM,
- vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
- unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
- ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
+ void *load_assembly_and_get_function_pointer = nullptr;
- if (Engine::get_singleton()->is_editor_hint()) {
- // Unhandled exceptions should not terminate the editor
- unhandled_exception_policy = POLICY_LOG_ERROR;
+ rc = hostfxr_get_runtime_delegate(cxt,
+ hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
}
-}
-
-void GDMono::add_assembly(int32_t p_domain_id, GDMonoAssembly *p_assembly) {
- assemblies[p_domain_id][p_assembly->get_name()] = p_assembly;
-}
-GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
- if (p_name == "mscorlib" && corlib_assembly) {
- return corlib_assembly;
- }
+ hostfxr_close(cxt);
- MonoDomain *domain = mono_domain_get();
- int32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
- GDMonoAssembly **result = assemblies[domain_id].getptr(p_name);
- return result ? *result : nullptr;
+ return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
-
-bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
#endif
- MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
- bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
- mono_assembly_name_free(aname);
- mono_free(aname);
-
- return result;
-}
-
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
+#ifdef TOOLS_ENABLED
+using godot_plugins_initialize_fn = bool (*)(void *, bool, gdmono::PluginCallbacks *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
+#else
+using godot_plugins_initialize_fn = bool (*)(void *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
#endif
- return load_assembly(p_name, p_aname, r_assembly, p_refonly, GDMonoAssembly::get_default_search_dirs());
-}
+#ifdef TOOLS_ENABLED
+godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
+ godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
-#endif
+ HostFxrCharString godot_plugins_path = str_to_hostfxr(
+ GodotSharpDirs::get_api_assemblies_dir().plus_file("GodotPlugins.dll"));
- print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
+ HostFxrCharString config_path = str_to_hostfxr(
+ GodotSharpDirs::get_api_assemblies_dir().plus_file("GodotPlugins.runtimeconfig.json"));
- GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, p_refonly, p_search_dirs);
+ load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
+ initialize_hostfxr_for_config(get_data(config_path));
+ ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr);
- if (!assembly) {
- return false;
- }
+ r_runtime_initialized = true;
- *r_assembly = assembly;
+ print_verbose(".NET: hostfxr initialized");
- print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
+ int rc = load_assembly_and_get_function_pointer(get_data(godot_plugins_path),
+ HOSTFXR_STR("GodotPlugins.Main, GodotPlugins"),
+ HOSTFXR_STR("InitializeFromEngine"),
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr,
+ (void **)&godot_plugins_initialize);
+ ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
- return true;
+ return godot_plugins_initialize;
}
+#else
+static String get_assembly_name() {
+ String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
-bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) {
- CRASH_COND(!r_assembly);
-
- print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
-
- GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly);
-
- if (!assembly) {
- return false;
+ if (assembly_name.is_empty()) {
+ assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
}
- *r_assembly = assembly;
-
- print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
-
- return true;
+ return assembly_name;
}
-ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) {
- ApiAssemblyInfo::Version api_assembly_version;
+godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
+ godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
- const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE
- ? BINDINGS_CLASS_NATIVECALLS
- : BINDINGS_CLASS_NATIVECALLS_EDITOR;
+ String assembly_name = get_assembly_name();
- GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name);
+ HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
+ .plus_file(assembly_name + ".dll"));
- if (nativecalls_klass) {
- GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
- if (api_hash_field) {
- api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(nullptr));
- }
+ load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
+ initialize_hostfxr_self_contained(get_data(assembly_path));
+ ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr);
- GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
- if (binds_ver_field) {
- api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(nullptr));
- }
+ r_runtime_initialized = true;
- GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
- if (cs_glue_ver_field) {
- api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(nullptr));
- }
- }
+ print_verbose(".NET: hostfxr initialized");
- return api_assembly_version;
-}
+ int rc = load_assembly_and_get_function_pointer(get_data(assembly_path),
+ str_to_hostfxr("GodotPlugins.Game.Main, " + assembly_name),
+ HOSTFXR_STR("InitializeFromGameProject"),
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr,
+ (void **)&godot_plugins_initialize);
+ ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
-String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) {
- return p_type == ApiAssemblyInfo::API_CORE ? "API_CORE" : "API_EDITOR";
+ return godot_plugins_initialize;
}
-bool GDMono::_load_corlib_assembly() {
- if (corlib_assembly) {
- return true;
- }
-
- bool success = load_assembly("mscorlib", &corlib_assembly);
-
- if (success) {
- GDMonoCache::update_corlib_cache();
- }
-
- return success;
-}
+godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) {
+ String assembly_name = get_assembly_name();
-#ifdef TOOLS_ENABLED
-bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config) {
- String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
- String dst_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
-
- String assembly_name = p_api_type == ApiAssemblyInfo::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+#if defined(WINDOWS_ENABLED)
+ String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".dll");
+#elif defined(MACOS_ENABLED)
+ String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".dylib");
+#elif defined(UNIX_ENABLED)
+ String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().plus_file(assembly_name + ".so");
+#else
+#error "Platform not supported (yet?)"
+#endif
- // Create destination directory if needed
- if (!DirAccess::exists(dst_dir)) {
- Ref<DirAccess> da = DirAccess::create_for_path(dst_dir);
- Error err = da->make_dir_recursive(dst_dir);
+ if (FileAccess::exists(native_aot_so_path)) {
+ Error err = OS::get_singleton()->open_dynamic_library(native_aot_so_path, r_aot_dll_handle);
if (err != OK) {
- ERR_PRINT("Failed to create destination directory for the API assemblies. Error: " + itos(err) + ".");
- return false;
+ return nullptr;
}
- }
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ void *lib = r_aot_dll_handle;
- String xml_file = assembly_name + ".xml";
- if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) {
- WARN_PRINT("Failed to copy '" + xml_file + "'.");
- }
+ void *symbol = nullptr;
- String pdb_file = assembly_name + ".pdb";
- if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) {
- WARN_PRINT("Failed to copy '" + pdb_file + "'.");
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "godotsharp_game_main_init", symbol);
+ ERR_FAIL_COND_V(err != OK, nullptr);
+ return (godot_plugins_initialize_fn)symbol;
}
- String assembly_file = assembly_name + ".dll";
- if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) {
- ERR_PRINT("Failed to copy '" + assembly_file + "'.");
- return false;
- }
-
- return true;
+ return nullptr;
}
+#endif
-static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool &r_out_of_sync) {
- String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-
- if (!FileAccess::exists(core_api_assembly_path) || !FileAccess::exists(editor_api_assembly_path)) {
- return false;
- }
-
- String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
+} // namespace
- if (!FileAccess::exists(cached_api_hash_path)) {
+static bool _on_core_api_assembly_loaded() {
+ if (!GDMonoCache::godot_api_cache_updated) {
return false;
}
- Ref<ConfigFile> cfg;
- cfg.instantiate();
- Error cfg_err = cfg->load(cached_api_hash_path);
- ERR_FAIL_COND_V(cfg_err != OK, false);
-
- // Checking the modified time is good enough
- if (FileAccess::get_modified_time(core_api_assembly_path) != (uint64_t)cfg->get_value("core", "modified_time") ||
- FileAccess::get_modified_time(editor_api_assembly_path) != (uint64_t)cfg->get_value("editor", "modified_time")) {
- return false;
- }
+ bool debug;
+#ifdef DEBUG_ENABLED
+ debug = true;
+#else
+ debug = false;
+#endif
- 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");
+ GDMonoCache::managed_callbacks.GD_OnCoreApiAssemblyLoaded(debug);
return true;
}
-static void create_cached_api_hash_for(const String &p_api_assemblies_dir) {
- String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
- String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
-
- Ref<ConfigFile> cfg;
- cfg.instantiate();
-
- 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());
-
- Error err = cfg->save(cached_api_hash_path);
- ERR_FAIL_COND(err != OK);
-}
-
-bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config) {
- MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.CheckApiAssemblies");
- ERR_FAIL_NULL_V(temp_domain, "Failed to create temporary domain to check API assemblies");
- _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain);
-
- _GDMONO_SCOPE_DOMAIN_(temp_domain);
-
- GDMono::LoadedApiAssembly temp_core_api_assembly;
- GDMono::LoadedApiAssembly temp_editor_api_assembly;
-
- if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly,
- p_config, /* refonly: */ true, /* loaded_callback: */ nullptr)) {
- return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync;
- }
-
- return true; // Failed to load, assume they're outdated assemblies
-}
-
-String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) {
-#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \
- ( \
- (m_out_of_sync ? String("The assembly is invalidated ") : String("The assembly was not found ")) + \
- (m_prebuilt_exists ? String("and the prebuilt assemblies are missing.") : String("and we failed to copy the prebuilt assemblies.")))
+void GDMono::initialize() {
+ print_verbose(".NET: Initializing module...");
- String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
+ _init_godot_api_hashes();
- String core_assembly_path = dst_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String editor_assembly_path = dst_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+ godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
- bool api_assemblies_out_of_sync = false;
+ if (!load_hostfxr(hostfxr_dll_handle)) {
+#if !defined(TOOLS_ENABLED)
+ godot_plugins_initialize = try_load_native_aot_library(hostfxr_dll_handle);
- 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;
- } 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)) {
- api_assemblies_out_of_sync = _temp_domain_load_are_assemblies_out_of_sync(p_config);
+ if (godot_plugins_initialize != nullptr) {
+ is_native_aot = true;
+ } else {
+ ERR_FAIL_MSG(".NET: Failed to load hostfxr");
}
+#else
+ ERR_FAIL_MSG(".NET: Failed to load hostfxr");
+#endif
}
- // Note: Even if only one of the assemblies if missing or out of sync, we update both
-
- if (!api_assemblies_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) {
- return String(); // No update needed
- }
-
- print_verbose("Updating '" + p_config + "' API assemblies");
-
- String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
- String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-
- if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) {
- return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ false);
+ if (!is_native_aot) {
+ godot_plugins_initialize = initialize_hostfxr_and_godot_plugins(runtime_initialized);
+ ERR_FAIL_NULL(godot_plugins_initialize);
}
- // Copy the prebuilt Api
- if (!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_CORE, p_config) ||
- !copy_prebuilt_api_assembly(ApiAssemblyInfo::API_EDITOR, p_config)) {
- return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ true);
- }
+ int32_t interop_funcs_size = 0;
+ const void **interop_funcs = godotsharp::get_runtime_interop_funcs(interop_funcs_size);
- // Cache the api hash of the assemblies we just copied
- create_cached_api_hash_for(dst_assemblies_dir);
+ GDMonoCache::ManagedCallbacks managed_callbacks{};
- return String(); // Updated successfully
+ void *godot_dll_handle = nullptr;
-#undef FAIL_REASON
-}
+#if defined(UNIX_ENABLED) && !defined(MACOS_ENABLED) && !defined(IOS_ENABLED)
+ // Managed code can access it on its own on other platforms
+ godot_dll_handle = dlopen(nullptr, RTLD_NOW);
#endif
-bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
- if (r_loaded_api_assembly.assembly) {
- return true;
- }
-
#ifdef TOOLS_ENABLED
- // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
-
- // If running the project manager, load it from the prebuilt API directory
- String assembly_dir = !Engine::get_singleton()->is_project_manager_hint()
- ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config)
- : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
-
- String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
-
- bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
+ gdmono::PluginCallbacks plugin_callbacks_res;
+ bool init_ok = godot_plugins_initialize(godot_dll_handle,
+ Engine::get_singleton()->is_editor_hint(),
+ &plugin_callbacks_res, &managed_callbacks,
+ interop_funcs, interop_funcs_size);
+ ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
+
+ plugin_callbacks = plugin_callbacks_res;
#else
- bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly);
+ bool init_ok = godot_plugins_initialize(godot_dll_handle, &managed_callbacks,
+ interop_funcs, interop_funcs_size);
+ ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
#endif
- 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;
- } else {
- r_loaded_api_assembly.out_of_sync = false;
- }
+ GDMonoCache::update_godot_api_cache(managed_callbacks);
- return success;
-}
+ print_verbose(".NET: GodotPlugins initialized");
-#ifdef TOOLS_ENABLED
-bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
- if (r_loaded_api_assembly.assembly) {
- return true;
- }
-
- // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
-
- // If running the project manager, load it from the prebuilt API directory
- String assembly_dir = !Engine::get_singleton()->is_project_manager_hint()
- ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config)
- : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
-
- String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-
- bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
-
- 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;
- } else {
- r_loaded_api_assembly.out_of_sync = false;
- }
-
- return success;
+ _on_core_api_assembly_loaded();
}
-#endif
-bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
- const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback) {
- if (!_load_core_api_assembly(r_core_api_assembly, p_config, p_refonly)) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load Core API assembly");
- }
- return false;
+#ifdef TOOLS_ENABLED
+void GDMono::initialize_load_assemblies() {
+ if (Engine::get_singleton()->is_project_manager_hint()) {
+ return;
}
-#ifdef TOOLS_ENABLED
- if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) {
+ // Load the project's main assembly. This doesn't necessarily need to succeed.
+ // The game may not be using .NET at all, or if the project does use .NET and
+ // we're running in the editor, it may just happen to be it wasn't built yet.
+ if (!_load_project_assembly()) {
if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load Editor API assembly");
+ print_error(".NET: Failed to load project assembly");
}
- return false;
}
-
- if (r_editor_api_assembly.out_of_sync) {
- return false;
- }
-#endif
-
- // Check if the core API assembly is out of sync only after trying to load the
- // editor API assembly. Otherwise, if both assemblies are out of sync, we would
- // only update the former as we won't know the latter also needs to be updated.
- if (r_core_api_assembly.out_of_sync) {
- return false;
- }
-
- if (p_callback) {
- return p_callback();
- }
-
- return true;
}
-
-bool GDMono::_on_core_api_assembly_loaded() {
- GDMonoCache::update_godot_api_cache();
-
- if (!GDMonoCache::cached_data.godot_api_cache_updated) {
- return false;
- }
-
- get_singleton()->_install_trace_listener();
-
- return true;
-}
-
-bool GDMono::_try_load_api_assemblies_preset() {
- return _try_load_api_assemblies(core_api_assembly, editor_api_assembly,
- get_expected_api_build_config(), /* refonly: */ false, _on_core_api_assembly_loaded);
-}
-
-void GDMono::_load_api_assemblies() {
- bool api_assemblies_loaded = _try_load_api_assemblies_preset();
-
-#if defined(TOOLS_ENABLED) && !defined(GD_MONO_SINGLE_APPDOMAIN)
- if (!api_assemblies_loaded) {
- // The API assemblies are out of sync or some other error happened. Fine, try one more time, but
- // this time update them from the prebuilt assemblies directory before trying to load them again.
-
- // Shouldn't happen. The project manager loads the prebuilt API assemblies
- CRASH_COND_MSG(Engine::get_singleton()->is_project_manager_hint(), "Failed to load one of the prebuilt API assemblies.");
-
- // 1. Unload the scripts domain
- Error domain_unload_err = _unload_scripts_domain();
- CRASH_COND_MSG(domain_unload_err != OK, "Mono: Failed to unload scripts domain.");
-
- // 2. Update the API assemblies
- String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync);
- CRASH_COND_MSG(!update_error.is_empty(), update_error);
-
- // 3. Load the scripts domain again
- Error domain_load_err = _load_scripts_domain();
- CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
-
- // 4. Try loading the updated assemblies
- api_assemblies_loaded = _try_load_api_assemblies_preset();
- }
#endif
- if (!api_assemblies_loaded) {
- // welp... too bad
-
- if (_are_api_assemblies_out_of_sync()) {
- if (core_api_assembly.out_of_sync) {
- ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync.");
- } else if (!GDMonoCache::cached_data.godot_api_cache_updated) {
- ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed.");
- }
+void GDMono::_init_godot_api_hashes() {
+#ifdef DEBUG_METHODS_ENABLED
+ get_api_core_hash();
#ifdef TOOLS_ENABLED
- if (editor_api_assembly.out_of_sync) {
- ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync.");
- }
-#endif
-
- CRASH_NOW();
- } else {
- CRASH_NOW_MSG("Failed to load one of the API assemblies.");
- }
- }
+ get_api_editor_hash();
+#endif // TOOLS_ENABLED
+#endif // DEBUG_METHODS_ENABLED
}
#ifdef TOOLS_ENABLED
-bool GDMono::_load_tools_assemblies() {
- if (tools_assembly && tools_project_editor_assembly) {
- return true;
- }
-
- bool success = load_assembly(TOOLS_ASM_NAME, &tools_assembly) &&
- load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly);
-
- return success;
-}
-#endif
-
bool GDMono::_load_project_assembly() {
- if (project_assembly) {
- return true;
- }
-
- String appname = ProjectSettings::get_singleton()->get("application/config/name");
- String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.is_empty()) {
- appname_safe = "UnnamedProject";
- }
-
- bool success = load_assembly(appname_safe, &project_assembly);
-
- if (success) {
- mono_assembly_set_main(project_assembly->get_assembly());
- CSharpLanguage::get_singleton()->lookup_scripts_in_assembly(project_assembly);
- }
-
- return success;
-}
+ String assembly_name = ProjectSettings::get_singleton()->get_setting("dotnet/project/assembly_name");
-void GDMono::_install_trace_listener() {
-#ifdef DEBUG_ENABLED
- // Install the trace listener now before the project assembly is loaded
- GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils");
- GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener");
-
- MonoException *exc = nullptr;
- install_func->invoke_raw(nullptr, nullptr, &exc);
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");
+ if (assembly_name.is_empty()) {
+ assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
}
-#endif
-}
-
-#ifndef GD_MONO_SINGLE_APPDOMAIN
-Error GDMono::_load_scripts_domain() {
- ERR_FAIL_COND_V(scripts_domain != nullptr, ERR_BUG);
-
- print_verbose("Mono: Loading scripts domain...");
-
- scripts_domain = GDMonoUtils::create_domain("GodotEngine.Domain.Scripts");
- ERR_FAIL_NULL_V_MSG(scripts_domain, ERR_CANT_CREATE, "Mono: Could not create scripts app domain.");
-
- mono_domain_set(scripts_domain, true);
-
- return OK;
-}
-
-Error GDMono::_unload_scripts_domain() {
- ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
-
- print_verbose("Mono: Finalizing scripts domain...");
-
- if (mono_domain_get() != root_domain) {
- mono_domain_set(root_domain, true);
- }
+ String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
+ .plus_file(assembly_name + ".dll");
+ assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
- finalizing_scripts_domain = true;
-
- if (!mono_domain_finalize(scripts_domain, 2000)) {
- ERR_PRINT("Mono: Domain finalization timeout.");
+ if (!FileAccess::exists(assembly_path)) {
+ return false;
}
- finalizing_scripts_domain = false;
-
- mono_gc_collect(mono_gc_max_generation());
-
- GDMonoCache::clear_godot_api_cache();
-
- _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
-
- core_api_assembly.assembly = nullptr;
-#ifdef TOOLS_ENABLED
- editor_api_assembly.assembly = nullptr;
-#endif
-
- project_assembly = nullptr;
-#ifdef TOOLS_ENABLED
- tools_assembly = nullptr;
- tools_project_editor_assembly = nullptr;
-#endif
-
- MonoDomain *domain = scripts_domain;
- scripts_domain = nullptr;
-
- print_verbose("Mono: Unloading scripts domain...");
+ String loaded_assembly_path;
+ bool success = plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16(), &loaded_assembly_path);
- MonoException *exc = nullptr;
- mono_domain_try_unload(domain, (MonoObject **)&exc);
-
- if (exc) {
- ERR_PRINT("Exception thrown when unloading scripts domain.");
- GDMonoUtils::debug_unhandled_exception(exc);
- return FAILED;
+ if (success) {
+ project_assembly_path = loaded_assembly_path.simplify_path();
+ project_assembly_modified_time = FileAccess::get_modified_time(loaded_assembly_path);
}
- return OK;
+ return success;
}
#endif
#ifdef GD_MONO_HOT_RELOAD
-Error GDMono::reload_scripts_domain() {
+Error GDMono::reload_project_assemblies() {
ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
- 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.");
+ finalizing_scripts_domain = true;
- // Load assemblies. The API and tools assemblies are required,
- // the application is aborted if these assemblies cannot be loaded.
+ CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload();
- _load_api_assemblies();
+ if (!get_plugin_callbacks().UnloadProjectPluginCallback()) {
+ ERR_FAIL_V_MSG(Error::FAILED, ".NET: Failed to unload assemblies.");
+ }
-#if defined(TOOLS_ENABLED)
- bool tools_assemblies_loaded = _load_tools_assemblies();
- CRASH_COND_MSG(!tools_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
-#endif
+ finalizing_scripts_domain = false;
// Load the project's main assembly. Here, during hot-reloading, we do
// consider failing to load the project's main assembly to be an error.
- // However, unlike the API and tools assemblies, the application can continue working.
if (!_load_project_assembly()) {
- print_error("Mono: Failed to load project assembly");
+ print_error(".NET: Failed to load project assembly.");
return ERR_CANT_OPEN;
}
@@ -1123,204 +558,38 @@ Error GDMono::reload_scripts_domain() {
}
#endif
-#ifndef GD_MONO_SINGLE_APPDOMAIN
-Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
- CRASH_COND(p_domain == nullptr);
- CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead
-
- String domain_name = mono_domain_get_friendly_name(p_domain);
-
- print_verbose("Mono: Unloading domain '" + domain_name + "'...");
-
- if (mono_domain_get() == p_domain) {
- mono_domain_set(root_domain, true);
- }
-
- if (!mono_domain_finalize(p_domain, 2000)) {
- ERR_PRINT("Mono: Domain finalization timeout.");
- }
-
- mono_gc_collect(mono_gc_max_generation());
-
- _domain_assemblies_cleanup(mono_domain_get_id(p_domain));
-
- MonoException *exc = nullptr;
- mono_domain_try_unload(p_domain, (MonoObject **)&exc);
-
- if (exc) {
- ERR_PRINT("Exception thrown when unloading domain '" + domain_name + "'.");
- GDMonoUtils::debug_print_unhandled_exception(exc);
- return FAILED;
- }
-
- return OK;
-}
-#endif
-
-GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
- MonoImage *image = mono_class_get_image(p_raw_class);
-
- if (image == corlib_assembly->get_image()) {
- return corlib_assembly->get_class(p_raw_class);
- }
-
- int32_t domain_id = mono_domain_get_id(mono_domain_get());
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
-
- for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
- GDMonoAssembly *assembly = E.value;
- if (assembly->get_image() == image) {
- GDMonoClass *klass = assembly->get_class(p_raw_class);
- if (klass) {
- return klass;
- }
- }
- }
-
- return nullptr;
-}
-
-GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) {
- GDMonoClass *klass = corlib_assembly->get_class(p_namespace, p_name);
- if (klass) {
- return klass;
- }
-
- int32_t domain_id = mono_domain_get_id(mono_domain_get());
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
-
- for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
- GDMonoAssembly *assembly = E.value;
- klass = assembly->get_class(p_namespace, p_name);
- if (klass) {
- return klass;
- }
- }
-
- return nullptr;
-}
-
-void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) {
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
-
- for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
- memdelete(E.value);
- }
-
- assemblies.erase(p_domain_id);
-}
-
-void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
- // This method will be called by the runtime when a thrown exception is not handled.
- // It won't be called when we manually treat a thrown exception as unhandled.
- // We assume the exception was already printed before calling this hook.
-
-#ifdef DEBUG_ENABLED
- GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
- if (EngineDebugger::is_active()) {
- EngineDebugger::get_singleton()->poll_events(false);
- }
-#endif
-
- exit(mono_environment_exitcode_get());
-
- GD_UNREACHABLE();
-}
-
GDMono::GDMono() {
singleton = this;
- gdmono_log = memnew(GDMonoLog);
-
runtime_initialized = false;
finalizing_scripts_domain = false;
- root_domain = nullptr;
- scripts_domain = nullptr;
-
- corlib_assembly = nullptr;
- project_assembly = nullptr;
-#ifdef TOOLS_ENABLED
- tools_assembly = nullptr;
- tools_project_editor_assembly = nullptr;
-#endif
-
api_core_hash = 0;
#ifdef TOOLS_ENABLED
api_editor_hash = 0;
#endif
-
- unhandled_exception_policy = POLICY_TERMINATE_APP;
}
GDMono::~GDMono() {
- if (is_runtime_initialized()) {
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- if (scripts_domain) {
- Error err = _unload_scripts_domain();
- if (err != OK) {
- ERR_PRINT("Mono: Failed to unload scripts domain.");
- }
- }
-#else
- CRASH_COND(scripts_domain != root_domain);
-
- print_verbose("Mono: Finalizing scripts domain...");
-
- if (mono_domain_get() != root_domain) {
- mono_domain_set(root_domain, true);
- }
-
- finalizing_scripts_domain = true;
-
- if (!mono_domain_finalize(root_domain, 2000)) {
- ERR_PRINT("Mono: Domain finalization timeout.");
- }
-
- finalizing_scripts_domain = false;
-
- mono_gc_collect(mono_gc_max_generation());
-
- GDMonoCache::clear_godot_api_cache();
-
- _domain_assemblies_cleanup(mono_domain_get_id(root_domain));
-
- core_api_assembly.assembly = nullptr;
-
- project_assembly = nullptr;
-
- root_domain = nullptr;
- scripts_domain = nullptr;
-
- // Leave the rest to 'mono_jit_cleanup'
-#endif
-
- for (const KeyValue<int32_t, HashMap<String, GDMonoAssembly *>> &E : assemblies) {
- const HashMap<String, GDMonoAssembly *> &domain_assemblies = E.value;
+ finalizing_scripts_domain = true;
- for (const KeyValue<String, GDMonoAssembly *> &F : domain_assemblies) {
- memdelete(F.value);
- }
+ if (is_runtime_initialized()) {
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.DisposablesTracker_OnGodotShuttingDown();
}
- assemblies.clear();
-
- print_verbose("Mono: Runtime cleanup...");
-
- mono_jit_cleanup(root_domain);
-
- print_verbose("Mono: Finalized");
+ }
- runtime_initialized = false;
+ if (hostfxr_dll_handle) {
+ OS::get_singleton()->close_dynamic_library(hostfxr_dll_handle);
}
+ finalizing_scripts_domain = false;
+ runtime_initialized = false;
+
#if defined(ANDROID_ENABLED)
gdmono::android::support::cleanup();
#endif
- if (gdmono_log) {
- memdelete(gdmono_log);
- }
-
singleton = nullptr;
}
@@ -1328,62 +597,7 @@ namespace mono_bind {
GodotSharp *GodotSharp::singleton = nullptr;
-void GodotSharp::attach_thread() {
- GDMonoUtils::attach_current_thread();
-}
-
-void GodotSharp::detach_thread() {
- GDMonoUtils::detach_current_thread();
-}
-
-int32_t GodotSharp::get_domain_id() {
- MonoDomain *domain = mono_domain_get();
- ERR_FAIL_NULL_V(domain, -1);
- return mono_domain_get_id(domain);
-}
-
-int32_t GodotSharp::get_scripts_domain_id() {
- ERR_FAIL_NULL_V_MSG(GDMono::get_singleton(),
- -1, "The Mono runtime is not initialized");
- MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain();
- ERR_FAIL_NULL_V(domain, -1);
- return mono_domain_get_id(domain);
-}
-
-bool GodotSharp::is_scripts_domain_loaded() {
- return GDMono::get_singleton() != nullptr &&
- GDMono::get_singleton()->is_runtime_initialized() &&
- GDMono::get_singleton()->get_scripts_domain() != nullptr;
-}
-
-bool GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
- return is_domain_finalizing_for_unload(p_domain_id);
-}
-
-bool GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) {
- return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id));
-}
-
-bool GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
- GDMono *gd_mono = GDMono::get_singleton();
-
- ERR_FAIL_COND_V_MSG(!gd_mono || !gd_mono->is_runtime_initialized(),
- false, "The Mono runtime is not initialized");
-
- ERR_FAIL_NULL_V(p_domain, true);
-
- if (p_domain == gd_mono->get_scripts_domain() && gd_mono->is_finalizing_scripts_domain()) {
- return true;
- }
-
- return mono_domain_is_unloading(p_domain);
-}
-
-bool GodotSharp::is_runtime_shutting_down() {
- return mono_runtime_is_shutting_down();
-}
-
-bool GodotSharp::is_runtime_initialized() {
+bool GodotSharp::_is_runtime_initialized() {
return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized();
}
@@ -1399,16 +613,7 @@ void GodotSharp::_reload_assemblies(bool p_soft_reload) {
}
void GodotSharp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("attach_thread"), &GodotSharp::attach_thread);
- ClassDB::bind_method(D_METHOD("detach_thread"), &GodotSharp::detach_thread);
-
- ClassDB::bind_method(D_METHOD("get_domain_id"), &GodotSharp::get_domain_id);
- ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &GodotSharp::get_scripts_domain_id);
- ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &GodotSharp::is_scripts_domain_loaded);
- ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &GodotSharp::_is_domain_finalizing_for_unload);
-
- ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &GodotSharp::is_runtime_shutting_down);
- ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::is_runtime_initialized);
+ ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::_is_runtime_initialized);
ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies);
}
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 51fd0f8483..43811a4325 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -34,131 +34,54 @@
#include "core/io/config_file.h"
#include "../godotsharp_defs.h"
-#include "gd_mono_assembly.h"
-#include "gd_mono_log.h"
-#ifdef WINDOWS_ENABLED
-#include "../utils/mono_reg_utils.h"
+#ifdef WIN32
+#define GD_CLR_STDCALL __stdcall
+#else
+#define GD_CLR_STDCALL
#endif
-namespace ApiAssemblyInfo {
-enum Type {
- API_CORE,
- API_EDITOR
-};
-
-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;
- }
-
- 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) {
- }
+namespace gdmono {
- static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type);
+#ifdef TOOLS_ENABLED
+struct PluginCallbacks {
+ using FuncLoadProjectAssemblyCallback = bool(GD_CLR_STDCALL *)(const char16_t *, String *);
+ using FuncLoadToolsAssemblyCallback = Object *(GD_CLR_STDCALL *)(const char16_t *, const void **, int32_t);
+ using FuncUnloadProjectPluginCallback = bool(GD_CLR_STDCALL *)();
+ FuncLoadProjectAssemblyCallback LoadProjectAssemblyCallback = nullptr;
+ FuncLoadToolsAssemblyCallback LoadToolsAssemblyCallback = nullptr;
+ FuncUnloadProjectPluginCallback UnloadProjectPluginCallback = nullptr;
};
+#endif
-String to_string(Type p_type);
-} // namespace ApiAssemblyInfo
-
-class GDMono {
-public:
- enum UnhandledExceptionPolicy {
- POLICY_TERMINATE_APP,
- POLICY_LOG_ERROR
- };
-
- struct LoadedApiAssembly {
- GDMonoAssembly *assembly = nullptr;
- bool out_of_sync = false;
+} // namespace gdmono
- LoadedApiAssembly() {}
- };
+#undef GD_CLR_STDCALL
-private:
+class GDMono {
bool runtime_initialized;
bool finalizing_scripts_domain;
- UnhandledExceptionPolicy unhandled_exception_policy;
+ void *hostfxr_dll_handle = nullptr;
+ bool is_native_aot = false;
- MonoDomain *root_domain = nullptr;
- MonoDomain *scripts_domain = nullptr;
+ String project_assembly_path;
+ uint64_t project_assembly_modified_time = 0;
- HashMap<int32_t, HashMap<String, GDMonoAssembly *>> assemblies;
-
- GDMonoAssembly *corlib_assembly = nullptr;
- GDMonoAssembly *project_assembly = nullptr;
#ifdef TOOLS_ENABLED
- GDMonoAssembly *tools_assembly = nullptr;
- GDMonoAssembly *tools_project_editor_assembly = nullptr;
-#endif
-
- LoadedApiAssembly core_api_assembly;
- LoadedApiAssembly editor_api_assembly;
-
- typedef bool (*CoreApiAssemblyLoadedCallback)();
-
- bool _are_api_assemblies_out_of_sync();
- bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config);
-
- bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
-#ifdef TOOLS_ENABLED
- bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
-#endif
-
- static bool _on_core_api_assembly_loaded();
-
- bool _load_corlib_assembly();
-#ifdef TOOLS_ENABLED
- bool _load_tools_assemblies();
-#endif
bool _load_project_assembly();
-
- bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
- const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback);
- bool _try_load_api_assemblies_preset();
- void _load_api_assemblies();
-
- void _install_trace_listener();
-
- void _register_internal_calls();
-
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- Error _load_scripts_domain();
- Error _unload_scripts_domain();
#endif
- void _domain_assemblies_cleanup(int32_t p_domain_id);
-
uint64_t api_core_hash;
#ifdef TOOLS_ENABLED
uint64_t api_editor_hash;
#endif
void _init_godot_api_hashes();
- void _init_exception_policy();
-
- GDMonoLog *gdmono_log = nullptr;
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- MonoRegInfo mono_reg_info;
+#ifdef TOOLS_ENABLED
+ gdmono::PluginCallbacks plugin_callbacks;
#endif
- void add_mono_shared_libs_dir_to_path();
- void determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir);
-
protected:
static GDMono *singleton;
@@ -192,107 +115,43 @@ public:
#endif
}
-#ifdef TOOLS_ENABLED
- bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config);
- String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = nullptr, const bool *p_editor_api_out_of_sync = nullptr);
-#endif
-
- static GDMono *get_singleton() { return singleton; }
-
- [[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
-
- UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
-
- // Do not use these, unless you know what you're doing
- void add_assembly(int32_t p_domain_id, GDMonoAssembly *p_assembly);
- GDMonoAssembly *get_loaded_assembly(const String &p_name);
-
- _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized && !mono_runtime_is_shutting_down() /* stays true after shutdown finished */; }
+ static GDMono *get_singleton() {
+ return singleton;
+ }
- _FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; }
+ _FORCE_INLINE_ bool is_runtime_initialized() const {
+ return runtime_initialized;
+ }
+ _FORCE_INLINE_ bool is_finalizing_scripts_domain() {
+ return finalizing_scripts_domain;
+ }
- _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
+ _FORCE_INLINE_ const String &get_project_assembly_path() const {
+ return project_assembly_path;
+ }
+ _FORCE_INLINE_ uint64_t get_project_assembly_modified_time() const {
+ return project_assembly_modified_time;
+ }
- _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
#ifdef TOOLS_ENABLED
- _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly.assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; }
-#endif
-
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; }
+ const gdmono::PluginCallbacks &get_plugin_callbacks() {
+ return plugin_callbacks;
+ }
#endif
- GDMonoClass *get_class(MonoClass *p_raw_class);
- GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
-
#ifdef GD_MONO_HOT_RELOAD
- Error reload_scripts_domain();
+ Error reload_project_assemblies();
#endif
- bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
- bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
- bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs);
- bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
-
- Error finalize_and_unload_domain(MonoDomain *p_domain);
-
void initialize();
+#ifdef TOOLS_ENABLED
void initialize_load_assemblies();
+#endif
GDMono();
~GDMono();
};
-namespace gdmono {
-
-class ScopeDomain {
- MonoDomain *prev_domain = nullptr;
-
-public:
- ScopeDomain(MonoDomain *p_domain) {
- prev_domain = mono_domain_get();
- if (prev_domain != p_domain) {
- mono_domain_set(p_domain, false);
- } else {
- prev_domain = nullptr;
- }
- }
-
- ~ScopeDomain() {
- if (prev_domain) {
- mono_domain_set(prev_domain, false);
- }
- }
-};
-
-class ScopeExitDomainUnload {
- MonoDomain *domain = nullptr;
-
-public:
- ScopeExitDomainUnload(MonoDomain *p_domain) :
- domain(p_domain) {
- }
-
- ~ScopeExitDomainUnload() {
- if (domain) {
- GDMono::get_singleton()->finalize_and_unload_domain(domain);
- }
- }
-};
-} // namespace gdmono
-
-#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
- gdmono::ScopeDomain __gdmono__scope__domain__(m_mono_domain); \
- (void)__gdmono__scope__domain__;
-
-#define _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(m_mono_domain) \
- gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \
- (void)__gdmono__scope__exit__domain__unload__;
-
namespace mono_bind {
class GodotSharp : public Object {
@@ -300,9 +159,8 @@ class GodotSharp : public Object {
friend class GDMono;
- bool _is_domain_finalizing_for_unload(int32_t p_domain_id);
-
void _reload_assemblies(bool p_soft_reload);
+ bool _is_runtime_initialized();
protected:
static GodotSharp *singleton;
@@ -311,20 +169,6 @@ protected:
public:
static GodotSharp *get_singleton() { return singleton; }
- void attach_thread();
- void detach_thread();
-
- int32_t get_domain_id();
- int32_t get_scripts_domain_id();
-
- bool is_scripts_domain_loaded();
-
- bool is_domain_finalizing_for_unload(int32_t p_domain_id);
- bool is_domain_finalizing_for_unload(MonoDomain *p_domain);
-
- bool is_runtime_shutting_down();
- bool is_runtime_initialized();
-
GodotSharp();
~GodotSharp();
};
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
deleted file mode 100644
index 42c6b6305f..0000000000
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ /dev/null
@@ -1,482 +0,0 @@
-/*************************************************************************/
-/* gd_mono_assembly.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 "gd_mono_assembly.h"
-
-#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/tokentype.h>
-
-#include "core/config/project_settings.h"
-#include "core/io/file_access.h"
-#include "core/io/file_access_pack.h"
-#include "core/os/os.h"
-#include "core/templates/list.h"
-
-#include "../godotsharp_dirs.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-
-Vector<String> GDMonoAssembly::search_dirs;
-
-void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
- String framework_dir;
-
- if (!p_custom_bcl_dir.is_empty()) {
- framework_dir = p_custom_bcl_dir;
- } else if (mono_assembly_getrootdir()) {
- framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5");
- }
-
- if (!framework_dir.is_empty()) {
- r_search_dirs.push_back(framework_dir);
- r_search_dirs.push_back(framework_dir.plus_file("Facades"));
- }
-
-#if !defined(TOOLS_ENABLED)
- String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir();
- if (!data_game_assemblies_dir.is_empty()) {
- r_search_dirs.push_back(data_game_assemblies_dir);
- }
-#endif
-
- if (p_custom_config.length()) {
- r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
- } else {
- r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
- }
-
- if (p_custom_config.is_empty()) {
- r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
- } else {
- String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug";
- r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
- }
-
- r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir());
- r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
- r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
-
-#ifdef TOOLS_ENABLED
- r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
-
- // For GodotTools to find the api assemblies
- r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"));
-#endif
-}
-
-// This is how these assembly loading hooks work:
-//
-// - The 'search' hook checks if the assembly has already been loaded, to avoid loading again.
-// - The 'preload' hook does the actual loading and is only called if the
-// 'search' hook didn't find the assembly in the list of loaded assemblies.
-// - The 'load' hook is called after the assembly has been loaded. Its job is to add the
-// assembly to the list of loaded assemblies so that the 'search' hook can look it up.
-
-void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] void *user_data) {
- String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
-
- MonoImage *image = mono_assembly_get_image(assembly);
-
- GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly));
-
-#ifdef GD_MONO_HOT_RELOAD
- String path = String::utf8(mono_image_get_filename(image));
- if (FileAccess::exists(path)) {
- gdassembly->modified_time = FileAccess::get_modified_time(path);
- }
-#endif
-
- MonoDomain *domain = mono_domain_get();
- GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
- return GDMonoAssembly::_search_hook(aname, user_data, false);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) {
- return GDMonoAssembly::_search_hook(aname, user_data, true);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
- return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
- return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
-}
-
-MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unused]] void *user_data, bool refonly) {
- String name = String::utf8(mono_assembly_name_get_name(aname));
- bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
-
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
- if (loaded_asm) {
- return loaded_asm->get_assembly();
- }
-
- return nullptr;
-}
-
-MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, [[maybe_unused]] void *user_data, bool refonly) {
- String name = String::utf8(mono_assembly_name_get_name(aname));
- return _load_assembly_search(name, aname, refonly, search_dirs);
-}
-
-MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
- MonoAssembly *res = nullptr;
- String path;
-
- bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
-
- for (int i = 0; i < p_search_dirs.size(); i++) {
- const String &search_dir = p_search_dirs[i];
-
- if (has_extension) {
- path = search_dir.plus_file(p_name);
- if (FileAccess::exists(path)) {
- res = _real_load_assembly_from(path, p_refonly, p_aname);
- if (res != nullptr) {
- return res;
- }
- }
- } else {
- path = search_dir.plus_file(p_name + ".dll");
- if (FileAccess::exists(path)) {
- res = _real_load_assembly_from(path, p_refonly, p_aname);
- if (res != nullptr) {
- return res;
- }
- }
-
- path = search_dir.plus_file(p_name + ".exe");
- if (FileAccess::exists(path)) {
- res = _real_load_assembly_from(path, p_refonly, p_aname);
- if (res != nullptr) {
- return res;
- }
- }
- }
- }
-
- return nullptr;
-}
-
-String GDMonoAssembly::find_assembly(const String &p_name) {
- String path;
-
- bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
-
- for (int i = 0; i < search_dirs.size(); i++) {
- const String &search_dir = search_dirs[i];
-
- if (has_extension) {
- path = search_dir.plus_file(p_name);
- if (FileAccess::exists(path)) {
- return path;
- }
- } else {
- path = search_dir.plus_file(p_name + ".dll");
- if (FileAccess::exists(path)) {
- return path;
- }
-
- path = search_dir.plus_file(p_name + ".exe");
- if (FileAccess::exists(path)) {
- return path;
- }
- }
- }
-
- return String();
-}
-
-void GDMonoAssembly::initialize() {
- fill_search_dirs(search_dirs);
-
- mono_install_assembly_search_hook(&assembly_search_hook, nullptr);
- mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, nullptr);
- mono_install_assembly_preload_hook(&assembly_preload_hook, nullptr);
- mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, nullptr);
- mono_install_assembly_load_hook(&assembly_load_hook, nullptr);
-}
-
-MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) {
- Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
- ERR_FAIL_COND_V_MSG(data.is_empty(), nullptr, "Could read the assembly in the specified location");
-
- String image_filename;
-
-#ifdef ANDROID_ENABLED
- if (p_path.begins_with("res://")) {
- image_filename = p_path.substr(6, p_path.length());
- } else {
- image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
- }
-#else
- // FIXME: globalize_path does not work on exported games
- image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
-#endif
-
- MonoImageOpenStatus status = MONO_IMAGE_OK;
-
- MonoImage *image = mono_image_open_from_data_with_name(
- (char *)&data[0], data.size(),
- true, &status, p_refonly,
- image_filename.utf8());
-
- ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from memory: '" + p_path + "'.");
-
- if (p_aname != nullptr) {
- // Check assembly version
- const MonoTableInfo *table = mono_image_get_table_info(image, MONO_TABLE_ASSEMBLY);
-
- ERR_FAIL_NULL_V(table, nullptr);
-
- if (mono_table_info_get_rows(table)) {
- uint32_t cols[MONO_ASSEMBLY_SIZE];
- mono_metadata_decode_row(table, 0, cols, MONO_ASSEMBLY_SIZE);
-
- // Not sure about .NET's policy. We will only ensure major and minor are equal, and ignore build and revision.
- uint16_t major = cols[MONO_ASSEMBLY_MAJOR_VERSION];
- uint16_t minor = cols[MONO_ASSEMBLY_MINOR_VERSION];
-
- uint16_t required_minor;
- uint16_t required_major = mono_assembly_name_get_version(p_aname, &required_minor, nullptr, nullptr);
-
- if (required_major != 0) {
- if (major != required_major && minor != required_minor) {
- mono_image_close(image);
- return nullptr;
- }
- }
- }
- }
-
-#ifdef DEBUG_ENABLED
- Vector<uint8_t> pdb_data;
- String pdb_path(p_path + ".pdb");
-
- if (!FileAccess::exists(pdb_path)) {
- pdb_path = p_path.get_basename() + ".pdb"; // without .dll
-
- if (!FileAccess::exists(pdb_path)) {
- goto no_pdb;
- }
- }
-
- pdb_data = FileAccess::get_file_as_array(pdb_path);
-
- // mono_debug_close_image doesn't seem to be needed
- mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
-
-no_pdb:
-
-#endif
-
- bool need_manual_load_hook = mono_image_get_assembly(image) != nullptr; // Re-using an existing image with an assembly loaded
-
- status = MONO_IMAGE_OK;
-
- MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly);
-
- ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image");
-
- if (need_manual_load_hook) {
- // For some reason if an assembly survived domain reloading (maybe because it's referenced somewhere else),
- // the mono internal search hook don't detect it, yet mono_image_open_from_data_with_name re-uses the image
- // and assembly, and mono_assembly_load_from_full doesn't call the load hook. We need to call it manually.
- String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
- bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
- if (!loaded_asm) {
- assembly_load_hook(assembly, nullptr);
- }
- }
-
- // Decrement refcount which was previously incremented by mono_image_open_from_data_with_name
- mono_image_close(image);
-
- return assembly;
-}
-
-void GDMonoAssembly::unload() {
- ERR_FAIL_NULL(image); // Should not be called if already unloaded
-
- for (const KeyValue<MonoClass *, GDMonoClass *> &E : cached_raw) {
- memdelete(E.value);
- }
-
- cached_classes.clear();
- cached_raw.clear();
-
- assembly = nullptr;
- image = nullptr;
-}
-
-String GDMonoAssembly::get_path() const {
- return String::utf8(mono_image_get_filename(image));
-}
-
-bool GDMonoAssembly::has_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, false);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoAssembly::get_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoAssembly::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
-
- attributes = mono_custom_attrs_from_assembly(assembly);
- attrs_fetched = true;
-}
-
-GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
- ERR_FAIL_NULL_V(image, nullptr);
-
- ClassKey key(p_namespace, p_name);
-
- GDMonoClass **match = cached_classes.getptr(key);
-
- if (match) {
- return *match;
- }
-
- MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
-
- if (!mono_class) {
- return nullptr;
- }
-
- GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
-
- cached_classes[key] = wrapped_class;
- cached_raw[mono_class] = wrapped_class;
-
- return wrapped_class;
-}
-
-GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
- ERR_FAIL_NULL_V(image, nullptr);
-
- HashMap<MonoClass *, GDMonoClass *>::Iterator match = cached_raw.find(p_mono_class);
-
- if (match) {
- return match->value;
- }
-
- StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
- StringName class_name = String::utf8(mono_class_get_name(p_mono_class));
-
- GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
-
- cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
- cached_raw[p_mono_class] = wrapped_class;
-
- return wrapped_class;
-}
-
-GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
- if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) {
- return GDMono::get_singleton()->get_corlib_assembly();
- }
-
- // We need to manually call the search hook in this case, as it won't be called in the next step
- MonoAssembly *assembly = mono_assembly_invoke_search_hook(p_aname);
-
- if (!assembly) {
- assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs);
- if (!assembly) {
- return nullptr;
- }
- }
-
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
- ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
- ERR_FAIL_COND_V(loaded_asm->get_assembly() != assembly, nullptr);
-
- return loaded_asm;
-}
-
-GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
- if (p_name == "mscorlib" || p_name == "mscorlib.dll") {
- return GDMono::get_singleton()->get_corlib_assembly();
- }
-
- // We need to manually call the search hook in this case, as it won't be called in the next step
- MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
- MonoAssembly *assembly = mono_assembly_invoke_search_hook(aname);
- mono_assembly_name_free(aname);
- mono_free(aname);
-
- if (!assembly) {
- assembly = _real_load_assembly_from(p_path, p_refonly);
- if (!assembly) {
- return nullptr;
- }
- }
-
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
- ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
-
- return loaded_asm;
-}
-
-GDMonoAssembly::~GDMonoAssembly() {
- if (image) {
- unload();
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
deleted file mode 100644
index 0a3ae6c4fe..0000000000
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*************************************************************************/
-/* gd_mono_assembly.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 GD_MONO_ASSEMBLY_H
-#define GD_MONO_ASSEMBLY_H
-
-#include <mono/jit/jit.h>
-#include <mono/metadata/assembly.h>
-
-#include "core/string/ustring.h"
-#include "core/templates/hash_map.h"
-#include "core/templates/rb_map.h"
-#include "gd_mono_utils.h"
-
-class GDMonoAssembly {
- struct ClassKey {
- struct Hasher {
- static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) {
- uint32_t hash = 0;
-
- GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash());
- GDMonoUtils::hash_combine(hash, p_key.class_name.hash());
-
- return hash;
- }
- };
-
- _FORCE_INLINE_ bool operator==(const ClassKey &p_a) const {
- return p_a.class_name == class_name && p_a.namespace_name == namespace_name;
- }
-
- ClassKey() {}
-
- ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) {
- namespace_name = p_namespace_name;
- class_name = p_class_name;
- }
-
- StringName namespace_name;
- StringName class_name;
- };
-
- String name;
- MonoImage *image = nullptr;
- MonoAssembly *assembly = nullptr;
-
- bool attrs_fetched = false;
- MonoCustomAttrInfo *attributes = nullptr;
-
-#ifdef GD_MONO_HOT_RELOAD
- uint64_t modified_time = 0;
-#endif
-
- HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
- HashMap<MonoClass *, GDMonoClass *> cached_raw;
-
- static Vector<String> search_dirs;
-
- static void assembly_load_hook(MonoAssembly *assembly, void *user_data);
- static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
- static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
- static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
- static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
-
- static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
- static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
-
- static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname = nullptr);
- static MonoAssembly *_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
-
- friend class GDMono;
- static void initialize();
-
-public:
- void unload();
-
- _FORCE_INLINE_ MonoImage *get_image() const { return image; }
- _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
- _FORCE_INLINE_ String get_name() const { return name; }
-
-#ifdef GD_MONO_HOT_RELOAD
- _FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; }
-#endif
-
- String get_path() const;
-
- bool has_attribute(GDMonoClass *p_attr_class);
- MonoObject *get_attribute(GDMonoClass *p_attr_class);
-
- void fetch_attributes();
-
- GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
- GDMonoClass *get_class(MonoClass *p_mono_class);
-
- static String find_assembly(const String &p_name);
-
- static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
- static const Vector<String> &get_default_search_dirs() { return search_dirs; }
-
- static GDMonoAssembly *load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
- static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
-
- GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly) :
- name(p_name),
- image(p_image),
- assembly(p_assembly) {
- }
- ~GDMonoAssembly();
-};
-
-#endif // GD_MONO_ASSEMBLY_H
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 69d8c7edc9..bfd803f326 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -30,310 +30,66 @@
#include "gd_mono_cache.h"
-#include "gd_mono.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_method.h"
-#include "gd_mono_utils.h"
+#include "core/error/error_macros.h"
namespace GDMonoCache {
-CachedData cached_data;
+ManagedCallbacks managed_callbacks;
+bool godot_api_cache_updated = false;
-#define CACHE_AND_CHECK(m_var, m_val) \
- { \
- CRASH_COND(m_var != nullptr); \
- m_var = m_val; \
- ERR_FAIL_COND_MSG(m_var == nullptr, "Mono Cache: Member " #m_var " is null."); \
- }
-
-#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)
+void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
+ int checked_count = 0;
-#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \
- { \
- CRASH_COND(!m_var.is_null()); \
- ERR_FAIL_COND_MSG(m_val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \
- m_var.set_from_method(m_val); \
- ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \
+#define CHECK_CALLBACK_NOT_NULL_IMPL(m_var, m_class, m_method) \
+ { \
+ ERR_FAIL_COND_MSG(m_var == nullptr, \
+ "Mono Cache: Managed callback for '" #m_class "_" #m_method "' is null."); \
+ checked_count += 1; \
}
-#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_METHOD_THUNK_AND_CHECK_IMPL(cached_data.methodthunk_##m_class##_##m_method, m_val)
-
-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;
- methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify();
- method_System_Diagnostics_StackTrace_ctor_bool = nullptr;
- method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr;
-#endif
-
- class_KeyNotFoundException = nullptr;
-}
-
-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
- class_DebuggingUtils = nullptr;
- methodthunk_DebuggingUtils_GetStackFrameInfo.nullify();
-#endif
-
- class_ExportAttribute = nullptr;
- field_ExportAttribute_hint = nullptr;
- field_ExportAttribute_hintString = nullptr;
- class_SignalAttribute = nullptr;
- class_ToolAttribute = nullptr;
- class_RPCAttribute = nullptr;
- property_RPCAttribute_Mode = nullptr;
- property_RPCAttribute_CallLocal = nullptr;
- property_RPCAttribute_TransferMode = nullptr;
- property_RPCAttribute_TransferChannel = nullptr;
- class_GodotMethodAttribute = nullptr;
- field_GodotMethodAttribute_methodName = nullptr;
- class_ScriptPathAttribute = nullptr;
- field_ScriptPathAttribute_path = nullptr;
- class_AssemblyHasScriptsAttribute = nullptr;
- field_AssemblyHasScriptsAttribute_requiresLookup = nullptr;
- 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_TrySerializeDelegate.nullify();
- methodthunk_DelegateUtils_TryDeserializeDelegate.nullify();
-
- // Start of MarshalUtils methods
-
- methodthunk_MarshalUtils_TypeIsGenericArray.nullify();
- methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify();
- methodthunk_MarshalUtils_TypeIsSystemGenericList.nullify();
- methodthunk_MarshalUtils_TypeIsSystemGenericDictionary.nullify();
- methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify();
- methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
- methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
- methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify();
-
- methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify();
-
- methodthunk_MarshalUtils_ArrayGetElementType.nullify();
- methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify();
-
- methodthunk_MarshalUtils_MakeGenericArrayType.nullify();
- methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify();
-
- // End of MarshalUtils methods
-
- task_scheduler_handle = Ref<MonoGCHandleRef>();
-}
-
-#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
-#define GODOT_API_NS_CLASS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class))
-
-void update_corlib_cache() {
- CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
- CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class()));
- CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class()));
- CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class()));
- CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class()));
- CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class()));
- CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class()));
- CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class()));
- CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class()));
- CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class()));
- CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class()));
- CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
- CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
- CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
-
- CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable"));
- CACHE_CLASS_AND_CHECK(System_Collections_ICollection, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "ICollection"));
- CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary"));
-
-#ifdef DEBUG_ENABLED
- CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
- CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames"));
- CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
- CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
-#endif
-
- CACHE_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", true));
-
- CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
-
- cached_data.corlib_cache_updated = true;
-}
-
-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
- CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
-#endif
-
- // Attributes
- CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
- CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
- CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
- CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
- CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
- CACHE_CLASS_AND_CHECK(RPCAttribute, GODOT_API_CLASS(RPCAttribute));
- CACHE_PROPERTY_AND_CHECK(RPCAttribute, Mode, CACHED_CLASS(RPCAttribute)->get_property("Mode"));
- CACHE_PROPERTY_AND_CHECK(RPCAttribute, CallLocal, CACHED_CLASS(RPCAttribute)->get_property("CallLocal"));
- CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferMode, CACHED_CLASS(RPCAttribute)->get_property("TransferMode"));
- CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferChannel, CACHED_CLASS(RPCAttribute)->get_property("TransferChannel"));
- CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
- CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
- CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute));
- CACHE_FIELD_AND_CHECK(ScriptPathAttribute, path, CACHED_CLASS(ScriptPathAttribute)->get_field("path"));
- CACHE_CLASS_AND_CHECK(AssemblyHasScriptsAttribute, GODOT_API_CLASS(AssemblyHasScriptsAttribute));
- CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, requiresLookup, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("requiresLookup"));
- 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, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2));
- CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2));
-
- // Start of MarshalUtils methods
-
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericList, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericList", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericDictionary", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1));
-
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2));
-
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3));
-
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2));
-
- // End of MarshalUtils methods
-
-#ifdef DEBUG_ENABLED
- CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4));
-#endif
-
- // 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);
+#define CHECK_CALLBACK_NOT_NULL(m_class, m_method) CHECK_CALLBACK_NOT_NULL_IMPL(p_managed_callbacks.m_class##_##m_method, m_class, m_method)
+
+ CHECK_CALLBACK_NOT_NULL(SignalAwaiter, SignalCallback);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, TrySerializeDelegateWithGCHandle);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, TryDeserializeDelegateWithGCHandle);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectBinding);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptNativeName);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SetGodotObjectPtr);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetOrCreateScriptBridgeForPath);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RemoveScriptBridge);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, TryReloadRegisteredScriptWithClass);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, UpdateScriptClassInfo);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SwapGCHandleForType);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetPropertyInfoList);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetPropertyDefaultValues);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Call);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Set);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Get);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallDispose);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallToString);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, HasMethodUnknownParams);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, SerializeState);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, DeserializeState);
+ CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle);
+ CHECK_CALLBACK_NOT_NULL(DebuggingUtils, GetCurrentStackInfo);
+ CHECK_CALLBACK_NOT_NULL(DisposablesTracker, OnGodotShuttingDown);
+ CHECK_CALLBACK_NOT_NULL(GD, OnCoreApiAssemblyLoaded);
+
+ managed_callbacks = p_managed_callbacks;
+
+ // It's easy to forget to add new callbacks here, so this should help
+ if (checked_count * sizeof(void *) != sizeof(ManagedCallbacks)) {
+ int missing_count = (sizeof(ManagedCallbacks) / sizeof(void *)) - checked_count;
+ WARN_PRINT("The presence of " + itos(missing_count) + " callback(s) was not validated");
+ }
- cached_data.godot_api_cache_updated = true;
+ godot_api_cache_updated = true;
}
} // namespace GDMonoCache
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index e9cc26899e..ca3a6c95a7 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -31,174 +31,120 @@
#ifndef GD_MONO_CACHE_H
#define GD_MONO_CACHE_H
-#include "gd_mono_header.h"
-#include "gd_mono_method_thunk.h"
+#include <stdint.h>
+
+#include "../csharp_script.h"
+#include "../interop_types.h"
+#include "../mono_gc_handle.h"
+#include "core/object/object.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
+#include "core/variant/callable.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/variant.h"
+
+class CSharpScript;
namespace GDMonoCache {
-struct CachedData {
- // -----------------------------------------------
- // corlib classes
-
- // 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;
- GDMonoMethodThunkR<MonoArray *, MonoObject *> methodthunk_System_Diagnostics_StackTrace_GetFrames;
- GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool = nullptr;
- GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr;
+#ifdef WIN32
+#define GD_CLR_STDCALL __stdcall
+#else
+#define GD_CLR_STDCALL
#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
- GDMonoClass *class_DebuggingUtils = nullptr;
- GDMonoMethodThunk<MonoObject *, MonoString **, int *, MonoString **> methodthunk_DebuggingUtils_GetStackFrameInfo;
-#endif
+struct godotsharp_property_info {
+ godot_string_name name; // Not owned
+ godot_string hint_string;
+ Variant::Type type;
+ PropertyHint hint;
+ PropertyUsageFlags usage;
+ bool exported;
+};
- GDMonoClass *class_ExportAttribute = nullptr;
- GDMonoField *field_ExportAttribute_hint = nullptr;
- GDMonoField *field_ExportAttribute_hintString = nullptr;
- GDMonoClass *class_SignalAttribute = nullptr;
- GDMonoClass *class_ToolAttribute = nullptr;
- GDMonoClass *class_RPCAttribute = nullptr;
- GDMonoProperty *property_RPCAttribute_Mode = nullptr;
- GDMonoProperty *property_RPCAttribute_CallLocal = nullptr;
- GDMonoProperty *property_RPCAttribute_TransferMode = nullptr;
- GDMonoProperty *property_RPCAttribute_TransferChannel = nullptr;
- GDMonoClass *class_GodotMethodAttribute = nullptr;
- GDMonoField *field_GodotMethodAttribute_methodName = nullptr;
- GDMonoClass *class_ScriptPathAttribute = nullptr;
- GDMonoField *field_ScriptPathAttribute_path = nullptr;
- GDMonoClass *class_AssemblyHasScriptsAttribute = nullptr;
- GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup = nullptr;
- GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes = nullptr;
-
- GDMonoField *field_GodotObject_ptr = nullptr;
- GDMonoField *field_StringName_ptr = nullptr;
- GDMonoField *field_NodePath_ptr = nullptr;
- GDMonoField *field_Image_ptr = nullptr;
- GDMonoField *field_RID_ptr = nullptr;
-
- GDMonoMethodThunk<MonoObject *> methodthunk_GodotObject_Dispose;
- GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr;
- GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr;
- GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback;
- GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate;
-
- GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals;
-
- GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate;
- GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate;
-
- // Start of MarshalUtils methods
-
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericList;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericDictionary;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute;
-
- GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition;
-
- GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType;
- GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
-
- GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType;
- GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType;
-
- // End of MarshalUtils methods
-
- Ref<MonoGCHandleRef> task_scheduler_handle;
-
- bool corlib_cache_updated;
- bool godot_api_cache_updated;
-
- void clear_corlib_cache();
- void clear_godot_api_cache();
-
- CachedData() {
- clear_corlib_cache();
- clear_godot_api_cache();
- }
+struct godotsharp_property_def_val_pair {
+ godot_string_name name; // Not owned
+ godot_variant value;
};
-extern CachedData cached_data;
+struct ManagedCallbacks {
+ using Callback_ScriptManagerBridge_GetPropertyInfoList_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, const String *, godotsharp_property_info *p_props, int32_t p_count);
+ using Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, godotsharp_property_def_val_pair *p_def_vals, int32_t p_count);
+
+ using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *);
+ using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, uint32_t, const Variant *);
+ using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr);
+ using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *);
+ using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *);
+ using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)();
+ using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *);
+ using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t);
+ using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
+ using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *);
+ using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *);
+ using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);
+ using FuncScriptManagerBridge_AddScriptBridge = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *);
+ using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
+ using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
+ using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
+ using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
+ using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
+ using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
+ using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);
+ using FuncCSharpInstanceBridge_Call = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, Callable::CallError *, Variant *);
+ using FuncCSharpInstanceBridge_Set = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant *);
+ using FuncCSharpInstanceBridge_Get = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, Variant *);
+ using FuncCSharpInstanceBridge_CallDispose = void(GD_CLR_STDCALL *)(GCHandleIntPtr, bool);
+ using FuncCSharpInstanceBridge_CallToString = void(GD_CLR_STDCALL *)(GCHandleIntPtr, String *, bool *);
+ using FuncCSharpInstanceBridge_HasMethodUnknownParams = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *);
+ using FuncCSharpInstanceBridge_SerializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
+ using FuncCSharpInstanceBridge_DeserializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
+ using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr);
+ using FuncDebuggingUtils_GetCurrentStackInfo = void(GD_CLR_STDCALL *)(Vector<ScriptLanguage::StackInfo> *);
+ using FuncDisposablesTracker_OnGodotShuttingDown = void(GD_CLR_STDCALL *)();
+ using FuncGD_OnCoreApiAssemblyLoaded = void(GD_CLR_STDCALL *)(bool);
+
+ FuncSignalAwaiter_SignalCallback SignalAwaiter_SignalCallback;
+ FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs;
+ FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals;
+ FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle;
+ FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle;
+ FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback;
+ FuncScriptManagerBridge_CreateManagedForGodotObjectBinding ScriptManagerBridge_CreateManagedForGodotObjectBinding;
+ FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
+ FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName;
+ FuncScriptManagerBridge_SetGodotObjectPtr ScriptManagerBridge_SetGodotObjectPtr;
+ FuncScriptManagerBridge_RaiseEventSignal ScriptManagerBridge_RaiseEventSignal;
+ FuncScriptManagerBridge_ScriptIsOrInherits ScriptManagerBridge_ScriptIsOrInherits;
+ FuncScriptManagerBridge_AddScriptBridge ScriptManagerBridge_AddScriptBridge;
+ FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
+ FuncScriptManagerBridge_RemoveScriptBridge ScriptManagerBridge_RemoveScriptBridge;
+ FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
+ FuncScriptManagerBridge_UpdateScriptClassInfo ScriptManagerBridge_UpdateScriptClassInfo;
+ FuncScriptManagerBridge_SwapGCHandleForType ScriptManagerBridge_SwapGCHandleForType;
+ FuncScriptManagerBridge_GetPropertyInfoList ScriptManagerBridge_GetPropertyInfoList;
+ FuncScriptManagerBridge_GetPropertyDefaultValues ScriptManagerBridge_GetPropertyDefaultValues;
+ FuncCSharpInstanceBridge_Call CSharpInstanceBridge_Call;
+ FuncCSharpInstanceBridge_Set CSharpInstanceBridge_Set;
+ FuncCSharpInstanceBridge_Get CSharpInstanceBridge_Get;
+ FuncCSharpInstanceBridge_CallDispose CSharpInstanceBridge_CallDispose;
+ FuncCSharpInstanceBridge_CallToString CSharpInstanceBridge_CallToString;
+ FuncCSharpInstanceBridge_HasMethodUnknownParams CSharpInstanceBridge_HasMethodUnknownParams;
+ FuncCSharpInstanceBridge_SerializeState CSharpInstanceBridge_SerializeState;
+ FuncCSharpInstanceBridge_DeserializeState CSharpInstanceBridge_DeserializeState;
+ FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle;
+ FuncDebuggingUtils_GetCurrentStackInfo DebuggingUtils_GetCurrentStackInfo;
+ FuncDisposablesTracker_OnGodotShuttingDown DisposablesTracker_OnGodotShuttingDown;
+ FuncGD_OnCoreApiAssemblyLoaded GD_OnCoreApiAssemblyLoaded;
+};
-void update_corlib_cache();
-void update_godot_api_cache();
+extern ManagedCallbacks managed_callbacks;
+extern bool godot_api_cache_updated;
-inline void clear_corlib_cache() {
- cached_data.clear_corlib_cache();
-}
+void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks);
-inline void clear_godot_api_cache() {
- cached_data.clear_godot_api_cache();
-}
} // namespace GDMonoCache
-#define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class)
-#define CACHED_CLASS_RAW(m_class) (GDMonoCache::cached_data.class_##m_class->get_mono_ptr())
-#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoCache::cached_data.rawclass_##m_class)
-#define CACHED_FIELD(m_class, m_field) (GDMonoCache::cached_data.field_##m_class##_##m_field)
-#define CACHED_METHOD(m_class, m_method) (GDMonoCache::cached_data.method_##m_class##_##m_method)
-#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method)
-#define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property)
+#undef GD_CLR_STDCALL
#endif // GD_MONO_CACHE_H
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
deleted file mode 100644
index 51c5aa3542..0000000000
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-/*************************************************************************/
-/* gd_mono_class.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 "gd_mono_class.h"
-
-#include <mono/metadata/attrdefs.h>
-#include <mono/metadata/debug-helpers.h>
-
-#include "gd_mono_assembly.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_marshal.h"
-
-String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
- // mono_type_get_full_name is not exposed to embedders, but this seems to do the job
- MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
-
- MonoException *exc = nullptr;
- MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return GDMonoMarshal::mono_string_to_godot(str);
-}
-
-MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
- return mono_class_get_type(p_mono_class);
-}
-
-String GDMonoClass::get_full_name() const {
- return get_full_name(mono_class);
-}
-
-String GDMonoClass::get_type_desc() const {
- return GDMonoUtils::get_type_desc(get_mono_type());
-}
-
-MonoType *GDMonoClass::get_mono_type() const {
- // Careful, you cannot compare two MonoType*.
- // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*?
- return get_mono_type(mono_class);
-}
-
-uint32_t GDMonoClass::get_flags() const {
- return mono_class_get_flags(mono_class);
-}
-
-bool GDMonoClass::is_static() const {
- uint32_t static_class_flags = MONO_TYPE_ATTR_ABSTRACT | MONO_TYPE_ATTR_SEALED;
- return (get_flags() & static_class_flags) == static_class_flags;
-}
-
-bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
- return mono_class_is_assignable_from(mono_class, p_from->mono_class);
-}
-
-StringName GDMonoClass::get_namespace() const {
- GDMonoClass *nesting_class = get_nesting_class();
- if (!nesting_class) {
- return namespace_name;
- }
- return nesting_class->get_namespace();
-}
-
-String GDMonoClass::get_name_for_lookup() const {
- GDMonoClass *nesting_class = get_nesting_class();
- if (!nesting_class) {
- return class_name;
- }
- return nesting_class->get_name_for_lookup() + "/" + class_name;
-}
-
-GDMonoClass *GDMonoClass::get_parent_class() const {
- MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
- return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : nullptr;
-}
-
-GDMonoClass *GDMonoClass::get_nesting_class() const {
- MonoClass *nesting_type = mono_class_get_nesting_type(mono_class);
- return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : nullptr;
-}
-
-#ifdef TOOLS_ENABLED
-Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
- bool class_is_enum = mono_class_is_enum(mono_class);
- ERR_FAIL_COND_V(!class_is_enum, Vector<MonoClassField *>());
-
- Vector<MonoClassField *> enum_fields;
-
- void *iter = nullptr;
- MonoClassField *raw_field = nullptr;
- while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != nullptr) {
- uint32_t field_flags = mono_field_get_flags(raw_field);
-
- // Enums have an instance field named value__ which holds the value of the enum.
- // Enum constants are static, so we will use this to ignore the value__ field.
- if (field_flags & MONO_FIELD_ATTR_PUBLIC && field_flags & MONO_FIELD_ATTR_STATIC) {
- enum_fields.push_back(raw_field);
- }
- }
-
- return enum_fields;
-}
-#endif
-
-bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, false);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoClass::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
-
- attributes = mono_custom_attrs_from_class(get_mono_ptr());
- attrs_fetched = true;
-}
-
-void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) {
- CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this));
-
- if (methods_fetched) {
- return;
- }
-
- void *iter = nullptr;
- MonoMethod *raw_method = nullptr;
- while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
- StringName name = String::utf8(mono_method_get_name(raw_method));
-
- // get_method implicitly fetches methods and adds them to this->methods
- GDMonoMethod *method = get_method(raw_method, name);
- ERR_CONTINUE(!method);
-
- if (method->get_name() != name) {
-#ifdef DEBUG_ENABLED
- String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")";
- WARN_PRINT("Method '" + fullname + "' is hidden by Godot API method. Should be '" +
- method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'.");
-#endif
- continue;
- }
-
-#ifdef DEBUG_ENABLED
- // For debug builds, we also fetched from native base classes as well before if this is not a native base class.
- // This allows us to warn the user here if they are using snake_case by mistake.
-
- if (p_native_base != this) {
- GDMonoClass *native_top = p_native_base;
- while (native_top) {
- GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count());
-
- if (m && m->get_name() != name) {
- // found
- String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")";
- WARN_PRINT("Method '" + fullname + "' should be '" + m->get_full_name_no_class() +
- "'. In class '" + namespace_name + "." + class_name + "'.");
- break;
- }
-
- if (native_top == CACHED_CLASS(GodotObject)) {
- break;
- }
-
- native_top = native_top->get_parent_class();
- }
- }
-#endif
-
- uint32_t flags = mono_method_get_flags(method->mono_method, nullptr);
-
- if (!(flags & MONO_METHOD_ATTR_VIRTUAL)) {
- continue;
- }
-
- // Virtual method of Godot Object derived type, let's try to find GodotMethod attribute
-
- GDMonoClass *top = p_native_base;
-
- while (top) {
- GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count());
-
- if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) {
- // Found base method with GodotMethod attribute.
- // We get the original API method name from this attribute.
- // This name must point to the virtual method.
-
- MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute));
-
- StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr);
-#ifdef DEBUG_ENABLED
- CRASH_COND(godot_method_name == StringName());
-#endif
- MethodKey key = MethodKey(godot_method_name, method->get_parameters_count());
- GDMonoMethod **existing_method = methods.getptr(key);
- if (existing_method) {
- memdelete(*existing_method); // Must delete old one
- }
- methods.insert(key, method);
-
- break;
- }
-
- if (top == CACHED_CLASS(GodotObject)) {
- break;
- }
-
- top = top->get_parent_class();
- }
- }
-
- methods_fetched = true;
-}
-
-GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
- ERR_FAIL_COND_V(!methods_fetched, nullptr);
-
- for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
- if (E.key.name == p_name) {
- return E.value;
- }
- }
-
- return nullptr;
-}
-
-bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
- return get_fetched_method_unknown_params(p_name) != nullptr;
-}
-
-bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
- return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr());
-}
-
-bool GDMonoClass::has_public_parameterless_ctor() {
- GDMonoMethod *ctor = get_method(".ctor", 0);
- return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC;
-}
-
-GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_params_count) {
- MethodKey key = MethodKey(p_name, p_params_count);
-
- GDMonoMethod **match = methods.getptr(key);
-
- if (match) {
- return *match;
- }
-
- if (methods_fetched) {
- return nullptr;
- }
-
- MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count);
-
- if (raw_method) {
- GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
- methods.insert(key, method);
-
- return method;
- }
-
- return nullptr;
-}
-
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
- MonoMethodSignature *sig = mono_method_signature(p_raw_method);
-
- int params_count = mono_signature_get_param_count(sig);
- StringName method_name = String::utf8(mono_method_get_name(p_raw_method));
-
- return get_method(p_raw_method, method_name, params_count);
-}
-
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) {
- MonoMethodSignature *sig = mono_method_signature(p_raw_method);
- int params_count = mono_signature_get_param_count(sig);
- return get_method(p_raw_method, p_name, params_count);
-}
-
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count) {
- ERR_FAIL_NULL_V(p_raw_method, nullptr);
-
- MethodKey key = MethodKey(p_name, p_params_count);
-
- GDMonoMethod **match = methods.getptr(key);
-
- if (match) {
- return *match;
- }
-
- GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
- methods.insert(key, method);
-
- return method;
-}
-
-GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) {
- MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace);
- MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
- mono_method_desc_free(desc);
-
- if (!method) {
- return nullptr;
- }
-
- ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr);
-
- return get_method(method);
-}
-
-GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
- HashMap<StringName, GDMonoField *>::Iterator result = fields.find(p_name);
-
- if (result) {
- return result->value;
- }
-
- if (fields_fetched) {
- return nullptr;
- }
-
- MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
-
- if (raw_field) {
- GDMonoField *field = memnew(GDMonoField(raw_field, this));
- fields.insert(p_name, field);
-
- return field;
- }
-
- return nullptr;
-}
-
-const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
- if (fields_fetched) {
- return fields_list;
- }
-
- void *iter = nullptr;
- MonoClassField *raw_field = nullptr;
- while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) {
- StringName name = String::utf8(mono_field_get_name(raw_field));
-
- HashMap<StringName, GDMonoField *>::Iterator match = fields.find(name);
-
- if (match) {
- fields_list.push_back(match->value);
- } else {
- GDMonoField *field = memnew(GDMonoField(raw_field, this));
- fields.insert(name, field);
- fields_list.push_back(field);
- }
- }
-
- fields_fetched = true;
-
- return fields_list;
-}
-
-GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
- HashMap<StringName, GDMonoProperty *>::Iterator result = properties.find(p_name);
-
- if (result) {
- return result->value;
- }
-
- if (properties_fetched) {
- return nullptr;
- }
-
- MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data());
-
- if (raw_property) {
- GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
- properties.insert(p_name, property);
-
- return property;
- }
-
- return nullptr;
-}
-
-const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
- if (properties_fetched) {
- return properties_list;
- }
-
- void *iter = nullptr;
- MonoProperty *raw_property = nullptr;
- while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) {
- StringName name = String::utf8(mono_property_get_name(raw_property));
-
- HashMap<StringName, GDMonoProperty *>::Iterator match = properties.find(name);
-
- if (match) {
- properties_list.push_back(match->value);
- } else {
- GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
- properties.insert(name, property);
- properties_list.push_back(property);
- }
- }
-
- properties_fetched = true;
-
- return properties_list;
-}
-
-const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
- if (delegates_fetched) {
- 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);
- }
-
- void *iter = nullptr;
- MonoClass *raw_class = nullptr;
- while ((raw_class = mono_class_get_nested_types(klass, &iter)) != nullptr) {
- if (mono_class_is_delegate(raw_class)) {
- StringName name = String::utf8(mono_class_get_name(raw_class));
-
- HashMap<StringName, GDMonoClass *>::Iterator match = delegates.find(name);
-
- if (match) {
- delegates_list.push_back(match->value);
- } else {
- GDMonoClass *delegate = memnew(GDMonoClass(String::utf8(mono_class_get_namespace(raw_class)), String::utf8(mono_class_get_name(raw_class)), raw_class, assembly));
- delegates.insert(name, delegate);
- delegates_list.push_back(delegate);
- }
- }
- }
-
- delegates_fetched = true;
-
- return delegates_list;
-}
-
-const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() {
- if (!method_list_fetched) {
- void *iter = nullptr;
- MonoMethod *raw_method = nullptr;
- while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
- method_list.push_back(memnew(GDMonoMethod(String::utf8(mono_method_get_name(raw_method)), raw_method)));
- }
-
- method_list_fetched = true;
- }
-
- return method_list;
-}
-
-GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) {
- namespace_name = p_namespace;
- class_name = p_name;
- mono_class = p_class;
- assembly = p_assembly;
-
- attrs_fetched = false;
- attributes = nullptr;
-
- methods_fetched = false;
- method_list_fetched = false;
- fields_fetched = false;
- properties_fetched = false;
- delegates_fetched = false;
-}
-
-GDMonoClass::~GDMonoClass() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-
- for (const KeyValue<StringName, GDMonoField *> &E : fields) {
- memdelete(E.value);
- }
-
- for (const KeyValue<StringName, GDMonoProperty *> &E : properties) {
- memdelete(E.value);
- }
-
- {
- // Ugly workaround...
- // We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods).
- // This way, we end with both the snake_case name and the PascalCasel name paired with the same method.
- // Therefore, we must avoid deleting the same pointer twice.
-
- int offset = 0;
- Vector<GDMonoMethod *> deleted_methods;
- deleted_methods.resize(methods.size());
-
- for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
- GDMonoMethod *method = E.value;
-
- if (method) {
- for (int i = 0; i < offset; i++) {
- if (deleted_methods[i] == method) {
- // Already deleted
- goto already_deleted;
- }
- }
-
- deleted_methods.write[offset] = method;
- ++offset;
-
- memdelete(method);
- }
-
- already_deleted:;
- }
-
- methods.clear();
- }
-
- for (int i = 0; i < method_list.size(); ++i) {
- memdelete(method_list[i]);
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
deleted file mode 100644
index 6b35da30f9..0000000000
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*************************************************************************/
-/* gd_mono_class.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 GD_MONO_CLASS_H
-#define GD_MONO_CLASS_H
-
-#include "core/string/ustring.h"
-#include "core/templates/rb_map.h"
-
-#include "gd_mono_field.h"
-#include "gd_mono_header.h"
-#include "gd_mono_method.h"
-#include "gd_mono_property.h"
-#include "gd_mono_utils.h"
-
-class GDMonoClass {
- struct MethodKey {
- struct Hasher {
- static _FORCE_INLINE_ uint32_t hash(const MethodKey &p_key) {
- uint32_t hash = 0;
-
- GDMonoUtils::hash_combine(hash, p_key.name.hash());
- GDMonoUtils::hash_combine(hash, HashMapHasherDefault::hash(p_key.params_count));
-
- return hash;
- }
- };
-
- _FORCE_INLINE_ bool operator==(const MethodKey &p_a) const {
- return p_a.params_count == params_count && p_a.name == name;
- }
-
- MethodKey() {}
-
- MethodKey(const StringName &p_name, uint16_t p_params_count) :
- name(p_name), params_count(p_params_count) {
- }
-
- StringName name;
- uint16_t params_count = 0;
- };
-
- StringName namespace_name;
- StringName class_name;
-
- MonoClass *mono_class = nullptr;
- GDMonoAssembly *assembly = nullptr;
-
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes = nullptr;
-
- // This contains both the original method names and remapped method names from the native Godot identifiers to the C# functions.
- // Most method-related functions refer to this and it's possible this is unintuitive for outside users; this may be a prime location for refactoring or renaming.
- bool methods_fetched;
- HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods;
-
- bool method_list_fetched;
- Vector<GDMonoMethod *> method_list;
-
- bool fields_fetched;
- HashMap<StringName, GDMonoField *> fields;
- Vector<GDMonoField *> fields_list;
-
- bool properties_fetched;
- HashMap<StringName, GDMonoProperty *> properties;
- Vector<GDMonoProperty *> properties_list;
-
- bool delegates_fetched;
- HashMap<StringName, GDMonoClass *> delegates;
- Vector<GDMonoClass *> delegates_list;
-
- friend class GDMonoAssembly;
- GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
-
-public:
- static String get_full_name(MonoClass *p_mono_class);
- static MonoType *get_mono_type(MonoClass *p_mono_class);
-
- String get_full_name() const;
- String get_type_desc() const;
- MonoType *get_mono_type() const;
-
- uint32_t get_flags() const;
- bool is_static() const;
-
- bool is_assignable_from(GDMonoClass *p_from) const;
-
- StringName get_namespace() const;
- _FORCE_INLINE_ StringName get_name() const { return class_name; }
- String get_name_for_lookup() const;
-
- _FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; }
- _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
-
- GDMonoClass *get_parent_class() const;
- GDMonoClass *get_nesting_class() const;
-
-#ifdef TOOLS_ENABLED
- Vector<MonoClassField *> get_enum_fields();
-#endif
-
- GDMonoMethod *get_fetched_method_unknown_params(const StringName &p_name);
- bool has_fetched_method_unknown_params(const StringName &p_name);
-
- bool has_attribute(GDMonoClass *p_attr_class);
- MonoObject *get_attribute(GDMonoClass *p_attr_class);
-
- void fetch_attributes();
- void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
-
- bool implements_interface(GDMonoClass *p_interface);
- bool has_public_parameterless_ctor();
-
- GDMonoMethod *get_method(const StringName &p_name, uint16_t p_params_count = 0);
- GDMonoMethod *get_method(MonoMethod *p_raw_method);
- GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
- GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count);
- GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
-
- GDMonoField *get_field(const StringName &p_name);
- const Vector<GDMonoField *> &get_all_fields();
-
- GDMonoProperty *get_property(const StringName &p_name);
- const Vector<GDMonoProperty *> &get_all_properties();
-
- const Vector<GDMonoClass *> &get_all_delegates();
-
- const Vector<GDMonoMethod *> &get_all_methods();
-
- ~GDMonoClass();
-};
-
-#endif // GD_MONO_CLASS_H
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
deleted file mode 100644
index cb025fc67a..0000000000
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ /dev/null
@@ -1,556 +0,0 @@
-/*************************************************************************/
-/* gd_mono_field.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 "gd_mono_field.h"
-
-#include <mono/metadata/attrdefs.h>
-
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) {
- mono_field_set_value(p_object, mono_field, p_value);
-}
-
-void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
- mono_field_set_value(p_object, mono_field, &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;
-
- 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;
- }
-
- 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;
- }
-}
-
-MonoObject *GDMonoField::get_value(MonoObject *p_object) {
- return mono_field_get_value_object(mono_domain_get(), mono_field, p_object);
-}
-
-bool GDMonoField::get_bool_value(MonoObject *p_object) {
- return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object));
-}
-
-int GDMonoField::get_int_value(MonoObject *p_object) {
- return GDMonoMarshal::unbox<int32_t>(get_value(p_object));
-}
-
-String GDMonoField::get_string_value(MonoObject *p_object) {
- MonoObject *val = get_value(p_object);
- return GDMonoMarshal::mono_string_to_godot((MonoString *)val);
-}
-
-bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, false);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoField::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
- attributes = mono_custom_attrs_from_field(owner->get_mono_ptr(), mono_field);
- attrs_fetched = true;
-}
-
-bool GDMonoField::is_static() {
- return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC;
-}
-
-IMonoClassMember::Visibility GDMonoField::get_visibility() {
- switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) {
- case MONO_FIELD_ATTR_PRIVATE:
- return IMonoClassMember::PRIVATE;
- case MONO_FIELD_ATTR_FAM_AND_ASSEM:
- return IMonoClassMember::PROTECTED_AND_INTERNAL;
- case MONO_FIELD_ATTR_ASSEMBLY:
- return IMonoClassMember::INTERNAL;
- case MONO_FIELD_ATTR_FAMILY:
- return IMonoClassMember::PROTECTED;
- case MONO_FIELD_ATTR_PUBLIC:
- return IMonoClassMember::PUBLIC;
- default:
- ERR_FAIL_V(IMonoClassMember::PRIVATE);
- }
-}
-
-GDMonoField::GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner) {
- owner = p_owner;
- mono_field = p_mono_field;
- name = String::utf8(mono_field_get_name(mono_field));
- MonoType *field_type = mono_field_get_type(mono_field);
- type.type_encoding = mono_type_get_type(field_type);
- MonoClass *field_type_class = mono_class_from_mono_type(field_type);
- type.type_class = GDMono::get_singleton()->get_class(field_type_class);
-
- attrs_fetched = false;
- attributes = nullptr;
-}
-
-GDMonoField::~GDMonoField() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
deleted file mode 100644
index 1d30f7a369..0000000000
--- a/modules/mono/mono_gd/gd_mono_field.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*************************************************************************/
-/* gd_mono_field.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 GD_MONO_FIELD_H
-#define GD_MONO_FIELD_H
-
-#include "gd_mono.h"
-#include "gd_mono_header.h"
-#include "i_mono_class_member.h"
-
-class GDMonoField : public IMonoClassMember {
- GDMonoClass *owner = nullptr;
- MonoClassField *mono_field = nullptr;
-
- StringName name;
- ManagedType type;
-
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes = nullptr;
-
-public:
- virtual GDMonoClass *get_enclosing_class() const final { return owner; }
-
- virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; }
-
- virtual StringName get_name() const final { return name; }
-
- virtual bool is_static() final;
- virtual Visibility get_visibility() final;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) final;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
- void fetch_attributes();
-
- _FORCE_INLINE_ ManagedType get_type() const { return type; }
-
- void set_value(MonoObject *p_object, MonoObject *p_value);
- void set_value_raw(MonoObject *p_object, void *p_ptr);
- void set_value_from_variant(MonoObject *p_object, const Variant &p_value);
-
- MonoObject *get_value(MonoObject *p_object);
-
- bool get_bool_value(MonoObject *p_object);
- int get_int_value(MonoObject *p_object);
- String get_string_value(MonoObject *p_object);
-
- GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner);
- ~GDMonoField();
-};
-
-#endif // GD_MONO_FIELD_H
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
deleted file mode 100644
index d206b0dfc3..0000000000
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*************************************************************************/
-/* gd_mono_internals.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 "gd_mono_internals.h"
-
-#include "../csharp_script.h"
-#include "../mono_gc_handle.h"
-#include "../utils/macros.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-#include "core/debugger/engine_debugger.h"
-#include "core/debugger/script_debugger.h"
-
-#include <mono/metadata/exception.h>
-
-namespace GDMonoInternals {
-void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
- // This method should not fail
-
- CRASH_COND(!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<RefCounted>(unmanaged);
-
- GDMonoClass *klass = GDMonoUtils::get_object_class(managed);
-
- CRASH_COND(!klass);
-
- GDMonoClass *native = GDMonoUtils::get_class_native_base(klass);
-
- CRASH_COND(native == nullptr);
-
- if (native == klass) {
- // If it's just a wrapper Godot class and not a custom inheriting class, then attach a
- // script binding instead. One of the advantages of this is that if a script is attached
- // later and it's not a C# script, then the managed object won't have to be disposed.
- // Another reason for doing this is that this instance could outlive CSharpLanguage, which would
- // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621
-
- CSharpScriptBinding script_binding;
-
- script_binding.inited = true;
- script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
- script_binding.wrapper_class = klass;
- script_binding.gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
- script_binding.owner = unmanaged;
-
- if (rc) {
- // 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(unmanaged));
-
- void *data;
- {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
- data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);
- }
-
- // Should be thread safe because the object was just created and nothing else should be referencing it
- CSharpLanguage::set_instance_binding(unmanaged, data);
-
- return;
- }
-
- MonoGCHandleData gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
-
- Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);
-
- CRASH_COND(script.is_null());
-
- CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
-
- unmanaged->set_script_and_instance(script, csharp_instance);
-}
-
-void unhandled_exception(MonoException *p_exc) {
- mono_print_unhandled_exception((MonoObject *)p_exc);
- gd_unhandled_exception_event(p_exc);
-
- if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
- // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
- mono_unhandled_exception((MonoObject *)p_exc);
- GDMono::unhandled_exception_hook((MonoObject *)p_exc, nullptr);
- GD_UNREACHABLE();
- } else {
-#ifdef DEBUG_ENABLED
- GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
- if (EngineDebugger::is_active()) {
- EngineDebugger::get_singleton()->poll_events(false);
- }
-#endif
- }
-}
-
-void gd_unhandled_exception_event(MonoException *p_exc) {
- MonoImage *mono_image = GDMono::get_singleton()->get_core_api_assembly()->get_image();
-
- MonoClass *gd_klass = mono_class_from_name(mono_image, "Godot", "GD");
- MonoMethod *unhandled_exception_method = mono_class_get_method_from_name(gd_klass, "OnUnhandledException", -1);
- void *args[1];
- args[0] = p_exc;
- mono_runtime_invoke(unhandled_exception_method, nullptr, (void **)args, nullptr);
-}
-} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
deleted file mode 100644
index a8f9cfa3ca..0000000000
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*************************************************************************/
-/* gd_mono_internals.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 GD_MONO_INTERNALS_H
-#define GD_MONO_INTERNALS_H
-
-#include <mono/jit/jit.h>
-
-#include "../utils/macros.h"
-
-#include "core/object/class_db.h"
-
-namespace GDMonoInternals {
-void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
-
-/**
- * Do not call this function directly.
- * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
- */
-void unhandled_exception(MonoException *p_exc);
-
-void gd_unhandled_exception_event(MonoException *p_exc);
-} // namespace GDMonoInternals
-
-#endif // GD_MONO_INTERNALS_H
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
deleted file mode 100644
index 6ea3c5539e..0000000000
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*************************************************************************/
-/* gd_mono_log.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 "gd_mono_log.h"
-
-#include <stdlib.h> // abort
-
-#include "core/io/dir_access.h"
-#include "core/os/os.h"
-
-#include "../godotsharp_dirs.h"
-#include "../utils/string_utils.h"
-
-static CharString get_default_log_level() {
-#ifdef DEBUG_ENABLED
- return String("info").utf8();
-#else
- return String("warning").utf8();
-#endif
-}
-
-GDMonoLog *GDMonoLog::singleton = nullptr;
-
-#ifdef GD_MONO_LOG_ENABLED
-
-static int get_log_level_id(const char *p_log_level) {
- const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", nullptr };
-
- int i = 0;
- while (valid_log_levels[i]) {
- if (!strcmp(valid_log_levels[i], p_log_level)) {
- return i;
- }
- i++;
- }
-
- return -1;
-}
-
-static String make_text(const char *log_domain, const char *log_level, const char *message) {
- String text(message);
- text += " (in domain ";
- text += log_domain;
- if (log_level) {
- text += ", ";
- text += log_level;
- }
- text += ")";
- return text;
-}
-
-void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) {
- if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) {
- String text = make_text(log_domain, log_level, message);
- text += "\n";
-
- GDMonoLog::get_singleton()->log_file->seek_end();
- GDMonoLog::get_singleton()->log_file->store_string(text);
- }
-
- if (fatal) {
- String text = make_text(log_domain, log_level, message);
- ERR_PRINT("Mono: FATAL ERROR '" + text + "', ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'.");
- // Make sure to flush before aborting
- GDMonoLog::get_singleton()->log_file->flush();
- GDMonoLog::get_singleton()->log_file.unref();
-
- abort();
- }
-}
-
-bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) {
- if (!DirAccess::exists(p_logs_dir)) {
- Ref<DirAccess> diraccess = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND_V(diraccess.is_null(), false);
- Error logs_mkdir_err = diraccess->make_dir_recursive(p_logs_dir);
- ERR_FAIL_COND_V_MSG(logs_mkdir_err != OK, false, "Failed to create mono logs directory.");
- }
-
- return true;
-}
-
-void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
- static const uint64_t MAX_SECS = 5 * 86400; // 5 days
-
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND(da.is_null());
-
- Error err = da->change_dir(p_logs_dir);
- ERR_FAIL_COND_MSG(err != OK, "Cannot change directory to '" + p_logs_dir + "'.");
-
- ERR_FAIL_COND(da->list_dir_begin() != OK);
-
- String current = da->get_next();
- while (!current.is_empty()) {
- if (da->current_is_dir() || !current.ends_with(".txt")) {
- current = da->get_next();
- continue;
- }
-
- uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current));
-
- if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) {
- da->remove(current);
- }
- current = da->get_next();
- }
-
- da->list_dir_end();
-}
-
-void GDMonoLog::initialize() {
- CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8();
-
- if (log_level.length() != 0 && get_log_level_id(log_level.get_data()) == -1) {
- ERR_PRINT(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): '" + log_level.get_data() + "'.");
- log_level = CharString();
- }
-
- if (log_level.length() == 0) {
- log_level = get_default_log_level();
- }
-
- String logs_dir = GodotSharpDirs::get_mono_logs_dir();
-
- if (_try_create_logs_dir(logs_dir)) {
- _delete_old_log_files(logs_dir);
-
- OS::Date date_now = OS::get_singleton()->get_date();
- OS::Time time_now = OS::get_singleton()->get_time();
-
- String log_file_name = str_format("%04d-%02d-%02d_%02d.%02d.%02d",
- (int)date_now.year, (int)date_now.month, (int)date_now.day,
- (int)time_now.hour, (int)time_now.minute, (int)time_now.second);
-
- log_file_name += str_format("_%d", OS::get_singleton()->get_process_id());
-
- log_file_name += ".log";
-
- log_file_path = logs_dir.plus_file(log_file_name);
-
- log_file = FileAccess::open(log_file_path, FileAccess::WRITE);
- if (log_file.is_null()) {
- ERR_PRINT("Mono: Cannot create log file at: " + log_file_path);
- }
- }
-
- mono_trace_set_level_string(log_level.get_data());
- log_level_id = get_log_level_id(log_level.get_data());
-
- if (log_file.is_valid()) {
- OS::get_singleton()->print("Mono: Log file is: '%s'\n", log_file_path.utf8().get_data());
- mono_trace_set_log_handler(mono_log_callback, this);
- } else {
- OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
- }
-}
-
-GDMonoLog::GDMonoLog() {
- singleton = this;
-}
-
-GDMonoLog::~GDMonoLog() {
- singleton = nullptr;
-}
-
-#else
-
-void GDMonoLog::initialize() {
- CharString log_level = get_default_log_level();
- mono_trace_set_level_string(log_level.get_data());
-}
-
-GDMonoLog::GDMonoLog() {
- singleton = this;
-}
-
-GDMonoLog::~GDMonoLog() {
- singleton = nullptr;
-}
-
-#endif // !defined(JAVASCRIPT_ENABLED)
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
deleted file mode 100644
index 93ba6a410e..0000000000
--- a/modules/mono/mono_gd/gd_mono_log.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*************************************************************************/
-/* gd_mono_log.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 GD_MONO_LOG_H
-#define GD_MONO_LOG_H
-
-#include <mono/utils/mono-logger.h>
-
-#include "core/typedefs.h"
-
-#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED)
-// We have custom mono log callbacks for WASM and iOS
-#define GD_MONO_LOG_ENABLED
-#endif
-
-#ifdef GD_MONO_LOG_ENABLED
-#include "core/io/file_access.h"
-#endif
-
-class GDMonoLog {
-#ifdef GD_MONO_LOG_ENABLED
- int log_level_id = -1;
-
- Ref<FileAccess> log_file;
- String log_file_path;
-
- bool _try_create_logs_dir(const String &p_logs_dir);
- void _delete_old_log_files(const String &p_logs_dir);
-
- static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data);
-#endif
-
- static GDMonoLog *singleton;
-
-public:
- _FORCE_INLINE_ static GDMonoLog *get_singleton() { return singleton; }
-
- void initialize();
-
- GDMonoLog();
- ~GDMonoLog();
-};
-
-#endif // GD_MONO_LOG_H
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
deleted file mode 100644
index a860442764..0000000000
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ /dev/null
@@ -1,1824 +0,0 @@
-/*************************************************************************/
-/* gd_mono_marshal.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 "gd_mono_marshal.h"
-
-#include "../signal_awaiter_utils.h"
-#include "gd_mono.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-
-namespace GDMonoMarshal {
-
-Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) {
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- return Variant::BOOL;
-
- case MONO_TYPE_I1:
- return Variant::INT;
- case MONO_TYPE_I2:
- return Variant::INT;
- case MONO_TYPE_I4:
- return Variant::INT;
- case MONO_TYPE_I8:
- return Variant::INT;
-
- case MONO_TYPE_U1:
- return Variant::INT;
- case MONO_TYPE_U2:
- return Variant::INT;
- case MONO_TYPE_U4:
- return Variant::INT;
- case MONO_TYPE_U8:
- return Variant::INT;
-
- case MONO_TYPE_R4:
- return Variant::FLOAT;
- case MONO_TYPE_R8:
- return Variant::FLOAT;
-
- case MONO_TYPE_STRING: {
- return Variant::STRING;
- } break;
-
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
- if (vtclass == CACHED_CLASS(Vector2)) {
- return Variant::VECTOR2;
- }
-
- if (vtclass == CACHED_CLASS(Vector2i)) {
- return Variant::VECTOR2I;
- }
-
- if (vtclass == CACHED_CLASS(Rect2)) {
- return Variant::RECT2;
- }
-
- if (vtclass == CACHED_CLASS(Rect2i)) {
- return Variant::RECT2I;
- }
-
- if (vtclass == CACHED_CLASS(Transform2D)) {
- return Variant::TRANSFORM2D;
- }
-
- if (vtclass == CACHED_CLASS(Vector3)) {
- return Variant::VECTOR3;
- }
-
- if (vtclass == CACHED_CLASS(Vector3i)) {
- return Variant::VECTOR3I;
- }
- if (vtclass == CACHED_CLASS(Vector4)) {
- return Variant::VECTOR4;
- }
-
- if (vtclass == CACHED_CLASS(Vector4i)) {
- return Variant::VECTOR4I;
- }
-
- if (vtclass == CACHED_CLASS(Basis)) {
- return Variant::BASIS;
- }
-
- if (vtclass == CACHED_CLASS(Quaternion)) {
- return Variant::QUATERNION;
- }
-
- if (vtclass == CACHED_CLASS(Transform3D)) {
- return Variant::TRANSFORM3D;
- }
- if (vtclass == CACHED_CLASS(Projection)) {
- return Variant::PROJECTION;
- }
- if (vtclass == CACHED_CLASS(AABB)) {
- return Variant::AABB;
- }
-
- if (vtclass == CACHED_CLASS(Color)) {
- return Variant::COLOR;
- }
-
- if (vtclass == CACHED_CLASS(Plane)) {
- return Variant::PLANE;
- }
-
- if (vtclass == CACHED_CLASS(Callable)) {
- return Variant::CALLABLE;
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- return Variant::SIGNAL;
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- return Variant::INT;
- }
- } break;
-
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoClass *elem_class = mono_class_get_element_class(p_type.type_class->get_mono_ptr());
-
- if (elem_class == CACHED_CLASS_RAW(MonoObject)) {
- return Variant::ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(uint8_t)) {
- return Variant::PACKED_BYTE_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(int32_t)) {
- return Variant::PACKED_INT32_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(int64_t)) {
- return Variant::PACKED_INT64_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(float)) {
- return Variant::PACKED_FLOAT32_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(double)) {
- return Variant::PACKED_FLOAT64_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(String)) {
- return Variant::PACKED_STRING_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(Vector2)) {
- return Variant::PACKED_VECTOR2_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(Vector3)) {
- return Variant::PACKED_VECTOR3_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(Color)) {
- return Variant::PACKED_COLOR_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(StringName)) {
- return Variant::ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(NodePath)) {
- return Variant::ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(RID)) {
- return Variant::ARRAY;
- }
-
- if (mono_class_is_enum(elem_class)) {
- return Variant::ARRAY;
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(elem_class);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- return Variant::ARRAY;
- }
- } break;
-
- case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = p_type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- return Variant::OBJECT;
- }
-
- if (CACHED_CLASS(StringName) == type_class) {
- return Variant::STRING_NAME;
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- return Variant::NODE_PATH;
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- return Variant::RID;
- }
-
- if (CACHED_CLASS(Dictionary) == type_class) {
- return Variant::DICTIONARY;
- }
-
- if (CACHED_CLASS(Array) == type_class) {
- return Variant::ARRAY;
- }
-
- // IDictionary
- if (p_type.type_class == CACHED_CLASS(System_Collections_IDictionary)) {
- return Variant::DICTIONARY;
- }
-
- // ICollection or IEnumerable
- if (p_type.type_class == CACHED_CLASS(System_Collections_ICollection) ||
- p_type.type_class == CACHED_CLASS(System_Collections_IEnumerable)) {
- return Variant::ARRAY;
- }
- } break;
-
- case MONO_TYPE_OBJECT: {
- if (r_nil_is_variant) {
- *r_nil_is_variant = true;
- }
- return Variant::NIL;
- } break;
-
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- return Variant::DICTIONARY;
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- return Variant::ARRAY;
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- return Variant::DICTIONARY;
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- return Variant::ARRAY;
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- return Variant::DICTIONARY;
- }
-
- // ICollection<T> or IEnumerable<T>
- 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 (r_nil_is_variant) {
- *r_nil_is_variant = false;
- }
-
- // Unknown
- return Variant::NIL;
-}
-
-bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
- switch (p_array_type.type_encoding) {
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoClass *elem_class = mono_class_get_element_class(p_array_type.type_class->get_mono_ptr());
- r_elem_type = ManagedType::from_class(elem_class);
- return true;
- } break;
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type());
-
- if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype) ||
- GDMonoUtils::Marshal::type_is_system_generic_list(array_reftype) ||
- GDMonoUtils::Marshal::type_is_generic_icollection(array_reftype) ||
- GDMonoUtils::Marshal::type_is_generic_ienumerable(array_reftype)) {
- MonoReflectionType *elem_reftype;
-
- GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype);
-
- r_elem_type = ManagedType::from_reftype(elem_reftype);
- return true;
- }
- } break;
- default: {
- } break;
- }
-
- return false;
-}
-
-MonoString *variant_to_mono_string(const Variant &p_var) {
- if (p_var.get_type() == Variant::NIL) {
- return nullptr; // Otherwise, Variant -> String would return the string "Null"
- }
- return mono_string_from_godot(p_var.operator String());
-}
-
-MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class) {
- MonoArrayType *array_type = mono_type_get_array_type(p_type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- return PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- return PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- return PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- return PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- return PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- return PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- return PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- return PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- return PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(StringName)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(RID)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) {
- return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass);
- }
-
- 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<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), p_type_class);
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- return GDMonoUtils::create_managed_from(p_var.operator Array(), p_type_class);
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- return Dictionary_to_system_generic_dict(p_var.operator Dictionary(), p_type_class, key_reftype, value_reftype);
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- return Array_to_system_generic_list(p_var.operator Array(), p_type_class, elem_reftype);
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
-
- return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), godot_dict_class);
- }
-
- // ICollection<T> or IEnumerable<T>
- if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
- MonoReflectionType *elem_reftype;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
-
- return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class);
- }
-
- // 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() + "'.");
-}
-
-MonoObject *variant_to_mono_object(const Variant &p_var) {
- // Variant
- switch (p_var.get_type()) {
- case Variant::BOOL: {
- MonoBoolean val = p_var.operator bool();
- return BOX_BOOLEAN(val);
- }
- case Variant::INT: {
- int64_t val = p_var.operator int64_t();
- return BOX_INT64(val);
- }
- case Variant::FLOAT: {
-#ifdef REAL_T_IS_DOUBLE
- double val = p_var.operator double();
- return BOX_DOUBLE(val);
-#else
- float val = p_var.operator float();
- return BOX_FLOAT(val);
-#endif
- }
- case Variant::STRING:
- return (MonoObject *)mono_string_from_godot(p_var.operator String());
- case Variant::VECTOR2: {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var.operator ::Vector2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
- }
- case Variant::VECTOR2I: {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var.operator ::Vector2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
- }
- case Variant::RECT2: {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var.operator ::Rect2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
- }
- case Variant::RECT2I: {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var.operator ::Rect2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
- }
- case Variant::VECTOR3: {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var.operator ::Vector3());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
- }
- case Variant::VECTOR3I: {
- GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var.operator ::Vector3i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
- }
- case Variant::TRANSFORM2D: {
- GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
- }
- case Variant::VECTOR4: {
- GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_var.operator ::Vector4());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4), &from);
- }
- case Variant::VECTOR4I: {
- GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_var.operator ::Vector4i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4i), &from);
- }
- case Variant::PLANE: {
- GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
- }
- case Variant::QUATERNION: {
- GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_var.operator ::Quaternion());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quaternion), &from);
- }
- case Variant::AABB: {
- GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var.operator ::AABB());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
- }
- case Variant::BASIS: {
- GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var.operator ::Basis());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
- }
- case Variant::TRANSFORM3D: {
- GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_var.operator ::Transform3D());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform3D), &from);
- }
- case Variant::PROJECTION: {
- GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_var.operator ::Projection());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Projection), &from);
- }
- case Variant::COLOR: {
- GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
- }
- case Variant::STRING_NAME:
- return GDMonoUtils::create_managed_from(p_var.operator StringName());
- case Variant::NODE_PATH:
- return GDMonoUtils::create_managed_from(p_var.operator NodePath());
- case Variant::RID:
- return GDMonoUtils::create_managed_from(p_var.operator ::RID());
- case Variant::OBJECT:
- return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
- case Variant::CALLABLE: {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
- }
- case Variant::SIGNAL: {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
- }
- case Variant::DICTIONARY:
- return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary));
- case Variant::ARRAY:
- return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array));
- case Variant::PACKED_BYTE_ARRAY:
- return (MonoObject *)PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
- case Variant::PACKED_INT32_ARRAY:
- return (MonoObject *)PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
- case Variant::PACKED_INT64_ARRAY:
- return (MonoObject *)PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
- case Variant::PACKED_FLOAT32_ARRAY:
- return (MonoObject *)PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
- case Variant::PACKED_FLOAT64_ARRAY:
- return (MonoObject *)PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
- case Variant::PACKED_STRING_ARRAY:
- return (MonoObject *)PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
- case Variant::PACKED_VECTOR2_ARRAY:
- return (MonoObject *)PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
- case Variant::PACKED_VECTOR3_ARRAY:
- return (MonoObject *)PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
- case Variant::PACKED_COLOR_ARRAY:
- return (MonoObject *)PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
- default:
- return nullptr;
- }
-}
-
-size_t variant_get_managed_unboxed_size(const ManagedType &p_type) {
- // This method prints no errors for unsupported types. It's called on all methods, not only
- // those that end up being invoked with Variant parameters.
-
- // For MonoObject* we return 0, as it doesn't need to be stored.
- constexpr size_t zero_for_mono_object = 0;
-
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- return sizeof(MonoBoolean);
- case MONO_TYPE_CHAR:
- return sizeof(uint16_t);
- case MONO_TYPE_I1:
- return sizeof(int8_t);
- case MONO_TYPE_I2:
- return sizeof(int16_t);
- case MONO_TYPE_I4:
- return sizeof(int32_t);
- case MONO_TYPE_I8:
- return sizeof(int64_t);
- case MONO_TYPE_U1:
- return sizeof(uint8_t);
- case MONO_TYPE_U2:
- return sizeof(uint16_t);
- case MONO_TYPE_U4:
- return sizeof(uint32_t);
- case MONO_TYPE_U8:
- return sizeof(uint64_t);
- case MONO_TYPE_R4:
- return sizeof(float);
- case MONO_TYPE_R8:
- return sizeof(double);
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
-#define RETURN_CHECK_FOR_STRUCT(m_struct) \
- if (vtclass == CACHED_CLASS(m_struct)) { \
- return sizeof(M_##m_struct); \
- }
-
- RETURN_CHECK_FOR_STRUCT(Vector2);
- RETURN_CHECK_FOR_STRUCT(Vector2i);
- RETURN_CHECK_FOR_STRUCT(Rect2);
- RETURN_CHECK_FOR_STRUCT(Rect2i);
- RETURN_CHECK_FOR_STRUCT(Transform2D);
- RETURN_CHECK_FOR_STRUCT(Vector3);
- RETURN_CHECK_FOR_STRUCT(Vector3i);
- RETURN_CHECK_FOR_STRUCT(Basis);
- RETURN_CHECK_FOR_STRUCT(Quaternion);
- RETURN_CHECK_FOR_STRUCT(Transform3D);
- RETURN_CHECK_FOR_STRUCT(AABB);
- RETURN_CHECK_FOR_STRUCT(Color);
- RETURN_CHECK_FOR_STRUCT(Plane);
- RETURN_CHECK_FOR_STRUCT(Callable);
- RETURN_CHECK_FOR_STRUCT(SignalInfo);
-
-#undef RETURN_CHECK_FOR_STRUCT
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
- switch (mono_type_get_type(enum_basetype)) {
- case MONO_TYPE_BOOLEAN:
- return sizeof(MonoBoolean);
- case MONO_TYPE_CHAR:
- return sizeof(uint16_t);
- case MONO_TYPE_I1:
- return sizeof(int8_t);
- case MONO_TYPE_I2:
- return sizeof(int16_t);
- case MONO_TYPE_I4:
- return sizeof(int32_t);
- case MONO_TYPE_I8:
- return sizeof(int64_t);
- case MONO_TYPE_U1:
- return sizeof(uint8_t);
- case MONO_TYPE_U2:
- return sizeof(uint16_t);
- case MONO_TYPE_U4:
- return sizeof(uint32_t);
- case MONO_TYPE_U8:
- return sizeof(uint64_t);
- default: {
- // Enum with unsupported base type. We return nullptr MonoObject* on error.
- return zero_for_mono_object;
- }
- }
- }
-
- // Enum with unsupported value type. We return nullptr MonoObject* on error.
- } break;
- case MONO_TYPE_STRING:
- return zero_for_mono_object;
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_GENERICINST:
- return zero_for_mono_object;
- case MONO_TYPE_OBJECT:
- return zero_for_mono_object;
- }
-
- // Unsupported type encoding. We return nullptr MonoObject* on error.
- return zero_for_mono_object;
-}
-
-void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) {
-#define RETURN_TYPE_VAL(m_type, m_val) \
- *reinterpret_cast<m_type *>(r_buffer) = m_val; \
- r_offset += sizeof(m_type); \
- return r_buffer;
-
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- RETURN_TYPE_VAL(MonoBoolean, (MonoBoolean)p_var.operator bool());
- case MONO_TYPE_CHAR:
- RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
- case MONO_TYPE_I1:
- RETURN_TYPE_VAL(int8_t, p_var.operator signed char());
- case MONO_TYPE_I2:
- RETURN_TYPE_VAL(int16_t, p_var.operator signed short());
- case MONO_TYPE_I4:
- RETURN_TYPE_VAL(int32_t, p_var.operator signed int());
- case MONO_TYPE_I8:
- RETURN_TYPE_VAL(int64_t, p_var.operator int64_t());
- case MONO_TYPE_U1:
- RETURN_TYPE_VAL(uint8_t, p_var.operator unsigned char());
- case MONO_TYPE_U2:
- RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
- case MONO_TYPE_U4:
- RETURN_TYPE_VAL(uint32_t, p_var.operator unsigned int());
- case MONO_TYPE_U8:
- RETURN_TYPE_VAL(uint64_t, p_var.operator uint64_t());
- case MONO_TYPE_R4:
- RETURN_TYPE_VAL(float, p_var.operator float());
- case MONO_TYPE_R8:
- RETURN_TYPE_VAL(double, p_var.operator double());
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
-#define RETURN_CHECK_FOR_STRUCT(m_struct) \
- if (vtclass == CACHED_CLASS(m_struct)) { \
- GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
- RETURN_TYPE_VAL(M_##m_struct, from); \
- }
-
- RETURN_CHECK_FOR_STRUCT(Vector2);
- RETURN_CHECK_FOR_STRUCT(Vector2i);
- RETURN_CHECK_FOR_STRUCT(Rect2);
- RETURN_CHECK_FOR_STRUCT(Rect2i);
- RETURN_CHECK_FOR_STRUCT(Transform2D);
- RETURN_CHECK_FOR_STRUCT(Vector3);
- RETURN_CHECK_FOR_STRUCT(Vector3i);
- RETURN_CHECK_FOR_STRUCT(Basis);
- RETURN_CHECK_FOR_STRUCT(Quaternion);
- RETURN_CHECK_FOR_STRUCT(Transform3D);
- RETURN_CHECK_FOR_STRUCT(AABB);
- RETURN_CHECK_FOR_STRUCT(Color);
- RETURN_CHECK_FOR_STRUCT(Plane);
-
-#undef RETURN_CHECK_FOR_STRUCT
-
- if (vtclass == CACHED_CLASS(Callable)) {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
- RETURN_TYPE_VAL(M_Callable, from);
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
- RETURN_TYPE_VAL(M_SignalInfo, from);
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
- switch (mono_type_get_type(enum_basetype)) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var.operator bool();
- RETURN_TYPE_VAL(MonoBoolean, val);
- }
- case MONO_TYPE_CHAR: {
- uint16_t val = p_var.operator unsigned short();
- RETURN_TYPE_VAL(uint16_t, val);
- }
- case MONO_TYPE_I1: {
- int8_t val = p_var.operator signed char();
- RETURN_TYPE_VAL(int8_t, val);
- }
- case MONO_TYPE_I2: {
- int16_t val = p_var.operator signed short();
- RETURN_TYPE_VAL(int16_t, val);
- }
- case MONO_TYPE_I4: {
- int32_t val = p_var.operator signed int();
- RETURN_TYPE_VAL(int32_t, val);
- }
- case MONO_TYPE_I8: {
- int64_t val = p_var.operator int64_t();
- RETURN_TYPE_VAL(int64_t, val);
- }
- case MONO_TYPE_U1: {
- uint8_t val = p_var.operator unsigned char();
- RETURN_TYPE_VAL(uint8_t, val);
- }
- case MONO_TYPE_U2: {
- uint16_t val = p_var.operator unsigned short();
- RETURN_TYPE_VAL(uint16_t, val);
- }
- case MONO_TYPE_U4: {
- uint32_t val = p_var.operator unsigned int();
- RETURN_TYPE_VAL(uint32_t, val);
- }
- case MONO_TYPE_U8: {
- uint64_t val = p_var.operator uint64_t();
- RETURN_TYPE_VAL(uint64_t, val);
- }
- default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'.");
- }
- }
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'.");
- } break;
-#undef RETURN_TYPE_VAL
- case MONO_TYPE_STRING:
- return variant_to_mono_string(p_var);
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- return variant_to_mono_array(p_var, p_type.type_class);
- case MONO_TYPE_CLASS:
- return variant_to_mono_object_of_class(p_var, p_type.type_class);
- case MONO_TYPE_GENERICINST:
- return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
- case MONO_TYPE_OBJECT:
- return variant_to_mono_object(p_var);
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + ".");
-}
-
-MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) {
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var.operator bool();
- return BOX_BOOLEAN(val);
- }
- case MONO_TYPE_CHAR: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_UINT16(val);
- }
- case MONO_TYPE_I1: {
- int8_t val = p_var.operator signed char();
- return BOX_INT8(val);
- }
- case MONO_TYPE_I2: {
- int16_t val = p_var.operator signed short();
- return BOX_INT16(val);
- }
- case MONO_TYPE_I4: {
- int32_t val = p_var.operator signed int();
- return BOX_INT32(val);
- }
- case MONO_TYPE_I8: {
- int64_t val = p_var.operator int64_t();
- return BOX_INT64(val);
- }
- case MONO_TYPE_U1: {
- uint8_t val = p_var.operator unsigned char();
- return BOX_UINT8(val);
- }
- case MONO_TYPE_U2: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_UINT16(val);
- }
- case MONO_TYPE_U4: {
- uint32_t val = p_var.operator unsigned int();
- return BOX_UINT32(val);
- }
- case MONO_TYPE_U8: {
- uint64_t val = p_var.operator uint64_t();
- return BOX_UINT64(val);
- }
- case MONO_TYPE_R4: {
- float val = p_var.operator float();
- return BOX_FLOAT(val);
- }
- case MONO_TYPE_R8: {
- double val = p_var.operator double();
- return BOX_DOUBLE(val);
- }
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
-#define RETURN_CHECK_FOR_STRUCT(m_struct) \
- if (vtclass == CACHED_CLASS(m_struct)) { \
- GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_struct), &from); \
- }
-
- RETURN_CHECK_FOR_STRUCT(Vector2);
- RETURN_CHECK_FOR_STRUCT(Vector2i);
- RETURN_CHECK_FOR_STRUCT(Rect2);
- RETURN_CHECK_FOR_STRUCT(Rect2i);
- RETURN_CHECK_FOR_STRUCT(Transform2D);
- RETURN_CHECK_FOR_STRUCT(Vector3);
- RETURN_CHECK_FOR_STRUCT(Vector3i);
- RETURN_CHECK_FOR_STRUCT(Basis);
- RETURN_CHECK_FOR_STRUCT(Quaternion);
- RETURN_CHECK_FOR_STRUCT(Transform3D);
- RETURN_CHECK_FOR_STRUCT(AABB);
- RETURN_CHECK_FOR_STRUCT(Color);
- RETURN_CHECK_FOR_STRUCT(Plane);
-
-#undef RETURN_CHECK_FOR_STRUCT
-
- if (vtclass == CACHED_CLASS(Callable)) {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
- MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
- switch (mono_type_get_type(enum_basetype)) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var.operator bool();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_CHAR: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I1: {
- int8_t val = p_var.operator signed char();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I2: {
- int16_t val = p_var.operator signed short();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I4: {
- int32_t val = p_var.operator signed int();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I8: {
- int64_t val = p_var.operator int64_t();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U1: {
- uint8_t val = p_var.operator unsigned char();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U2: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U4: {
- uint32_t val = p_var.operator unsigned int();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U8: {
- uint64_t val = p_var.operator uint64_t();
- return BOX_ENUM(enum_baseclass, val);
- }
- default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(enum_baseclass) + "'.");
- }
- }
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'.");
- } break;
- case MONO_TYPE_STRING:
- return (MonoObject *)variant_to_mono_string(p_var);
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- return (MonoObject *)variant_to_mono_array(p_var, p_type.type_class);
- case MONO_TYPE_CLASS:
- return variant_to_mono_object_of_class(p_var, p_type.type_class);
- case MONO_TYPE_GENERICINST:
- return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
- case MONO_TYPE_OBJECT:
- return variant_to_mono_object(p_var);
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + ".");
-}
-
-Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) {
- ERR_FAIL_COND_V(!p_type.type_class, Variant());
-
-#ifdef DEBUG_ENABLED
- CRASH_COND_MSG(p_type.type_encoding == MONO_TYPE_OBJECT, "Type of object should be known.");
-#endif
-
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- return (bool)unbox<MonoBoolean>(p_obj);
- case MONO_TYPE_CHAR:
- return unbox<uint16_t>(p_obj);
- case MONO_TYPE_I1:
- return unbox<int8_t>(p_obj);
- case MONO_TYPE_I2:
- return unbox<int16_t>(p_obj);
- case MONO_TYPE_I4:
- return unbox<int32_t>(p_obj);
- case MONO_TYPE_I8:
- return unbox<int64_t>(p_obj);
- case MONO_TYPE_U1:
- return unbox<uint8_t>(p_obj);
- case MONO_TYPE_U2:
- return unbox<uint16_t>(p_obj);
- case MONO_TYPE_U4:
- return unbox<uint32_t>(p_obj);
- case MONO_TYPE_U8:
- return unbox<uint64_t>(p_obj);
- case MONO_TYPE_R4:
- return unbox<float>(p_obj);
- case MONO_TYPE_R8:
- return unbox<double>(p_obj);
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
- if (vtclass == CACHED_CLASS(Vector2)) {
- return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Vector2i)) {
- return MARSHALLED_IN(Vector2i, unbox_addr<GDMonoMarshal::M_Vector2i>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Rect2)) {
- return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Rect2i)) {
- return MARSHALLED_IN(Rect2i, unbox_addr<GDMonoMarshal::M_Rect2i>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Transform2D)) {
- return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Vector3)) {
- return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Vector3i)) {
- return MARSHALLED_IN(Vector3i, unbox_addr<GDMonoMarshal::M_Vector3i>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Basis)) {
- return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Quaternion)) {
- return MARSHALLED_IN(Quaternion, unbox_addr<GDMonoMarshal::M_Quaternion>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Transform3D)) {
- return MARSHALLED_IN(Transform3D, unbox_addr<GDMonoMarshal::M_Transform3D>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(AABB)) {
- return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Color)) {
- return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Plane)) {
- return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Callable)) {
- return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- return managed_to_signal_info(unbox<GDMonoMarshal::M_SignalInfo>(p_obj));
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- return unbox<int32_t>(p_obj);
- }
- } break;
- case MONO_TYPE_STRING: {
- if (p_obj == nullptr) {
- return Variant(); // NIL
- }
- return mono_string_to_godot_not_null((MonoString *)p_obj);
- } break;
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- return mono_array_to_PackedByteArray((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- return mono_array_to_PackedInt32Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- return mono_array_to_PackedInt64Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- return mono_array_to_PackedFloat32Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- return mono_array_to_PackedFloat64Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- return mono_array_to_PackedStringArray((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- return mono_array_to_PackedVector2Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- return mono_array_to_PackedVector3Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- return mono_array_to_PackedColorArray((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(StringName)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(RID)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (p_fail_with_err) {
- ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant.");
- } else {
- return Variant();
- }
- } break;
- case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = p_type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
- if (ptr != nullptr) {
- RefCounted *rc = Object::cast_to<RefCounted>(ptr);
- return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr);
- }
- return Variant();
- }
-
- if (CACHED_CLASS(StringName) == type_class) {
- StringName *ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_obj));
- return ptr ? Variant(*ptr) : Variant();
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj));
- return ptr ? Variant(*ptr) : Variant();
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
- return ptr ? Variant(*ptr) : Variant();
- }
-
- // Godot.Collections.Dictionary
- if (CACHED_CLASS(Dictionary) == type_class) {
- MonoException *exc = nullptr;
- Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return ptr ? Variant(*ptr) : Variant();
- }
-
- // Godot.Collections.Array
- if (CACHED_CLASS(Array) == type_class) {
- MonoException *exc = nullptr;
- Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return ptr ? Variant(*ptr) : Variant();
- }
- } break;
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- MonoException *exc = nullptr;
- MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return *unbox<Dictionary *>(ret);
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- MonoException *exc = nullptr;
- MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return *unbox<Array *>(ret);
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- return system_generic_dict_to_Dictionary(p_obj, p_type.type_class, key_reftype, value_reftype);
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- return system_generic_list_to_Array_variant(p_obj, p_type.type_class, elem_reftype);
- }
-
- // GodotObject
- GDMonoClass *type_class = p_type.type_class;
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
- if (ptr != nullptr) {
- RefCounted *rc = Object::cast_to<RefCounted>(ptr);
- return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr);
- }
- return Variant();
- }
- } break;
- }
-
- if (p_fail_with_err) {
- ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" + p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
- } else {
- return Variant();
- }
-}
-
-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);
-}
-
-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);
-}
-
-String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
- if (p_obj == nullptr) {
- 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);
-
- 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();
- }
-}
-
-MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
- String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
- ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
- GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
- CRASH_COND(ctor == nullptr);
-
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype);
- MonoObject *godot_dict = GDMonoUtils::create_managed_from(p_dict, godot_dict_class);
-
- void *ctor_args[1] = { godot_dict };
-
- MonoException *exc = nullptr;
- ctor->invoke_raw(mono_object, ctor_args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return mono_object;
-}
-
-Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype);
- String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
- ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
- GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true);
- CRASH_COND(godot_dict_ctor == nullptr);
-
- MonoObject *godot_dict = mono_object_new(mono_domain_get(), godot_dict_class->get_mono_ptr());
- ERR_FAIL_NULL_V(godot_dict, Dictionary());
-
- void *ctor_args[1] = { p_obj };
-
- MonoException *exc = nullptr;
- godot_dict_ctor->invoke_raw(godot_dict, ctor_args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- exc = nullptr;
- MonoObject *ret = godot_dict_class->get_method("GetPtr")->invoke(godot_dict, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return *unbox<Dictionary *>(ret);
-}
-
-MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) {
- MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype);
-
- String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)";
- GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
- CRASH_COND(ctor == nullptr);
-
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(p_elem_reftype);
- MonoObject *godot_array = GDMonoUtils::create_managed_from(p_array, godot_array_class);
-
- void *ctor_args[1] = { godot_array };
-
- MonoException *exc = nullptr;
- ctor->invoke_raw(mono_object, ctor_args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return mono_object;
-}
-
-Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) {
- GDMonoMethod *to_array = p_class->get_method("ToArray", 0);
- CRASH_COND(to_array == nullptr);
-
- MonoException *exc = nullptr;
- MonoObject *array = to_array->invoke_raw(p_obj, nullptr, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- ERR_FAIL_NULL_V(array, Variant());
-
- ManagedType type = ManagedType::from_class(mono_object_get_class(array));
-
- bool result_is_array = type.type_encoding != MONO_TYPE_SZARRAY && type.type_encoding != MONO_TYPE_ARRAY;
- ERR_FAIL_COND_V(result_is_array, Variant());
-
- return mono_object_to_variant(array, type);
-}
-
-MonoArray *Array_to_mono_array(const Array &p_array) {
- int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length);
-
- for (int i = 0; i < length; i++) {
- MonoObject *boxed = variant_to_mono_object(p_array[i]);
- mono_array_setref(ret, i, boxed);
- }
-
- 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) {
- 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 *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();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length);
-
- for (int i = 0; i < length; i++) {
- MonoString *boxed = mono_string_from_godot(r[i]);
- mono_array_setref(ret, i, boxed);
- }
-
- return ret;
-}
-
-PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) {
- PackedStringArray ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- String *w = ret.ptrw();
-
- for (int i = 0; i < length; i++) {
- MonoString *elem = mono_array_get(p_array, MonoString *, i);
- w[i] = mono_string_to_godot(elem);
- }
-
- return ret;
-}
-
-MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) {
- const Color *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length);
-
- if constexpr (InteropLayout::MATCHES_Color) {
- Color *dst = mono_array_addr(ret, Color, 0);
- memcpy(dst, src, length * sizeof(Color));
- } else {
- for (int i = 0; i < length; i++) {
- M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
- *raw = MARSHALLED_OUT(Color, src[i]);
- }
- }
-
- return ret;
-}
-
-PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) {
- PackedColorArray ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- Color *dst = ret.ptrw();
-
- if constexpr (InteropLayout::MATCHES_Color) {
- const Color *src = mono_array_addr(p_array, Color, 0);
- memcpy(dst, src, length * sizeof(Color));
- } else {
- for (int i = 0; i < length; i++) {
- dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
- }
- }
-
- return ret;
-}
-
-MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) {
- const Vector2 *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length);
-
- if constexpr (InteropLayout::MATCHES_Vector2) {
- Vector2 *dst = mono_array_addr(ret, Vector2, 0);
- memcpy(dst, src, length * sizeof(Vector2));
- } else {
- for (int i = 0; i < length; i++) {
- M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
- *raw = MARSHALLED_OUT(Vector2, src[i]);
- }
- }
-
- return ret;
-}
-
-PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) {
- PackedVector2Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- Vector2 *dst = ret.ptrw();
-
- if constexpr (InteropLayout::MATCHES_Vector2) {
- const Vector2 *src = mono_array_addr(p_array, Vector2, 0);
- memcpy(dst, src, length * sizeof(Vector2));
- } else {
- for (int i = 0; i < length; i++) {
- dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
- }
- }
-
- return ret;
-}
-
-MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) {
- const Vector3 *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length);
-
- if constexpr (InteropLayout::MATCHES_Vector3) {
- Vector3 *dst = mono_array_addr(ret, Vector3, 0);
- memcpy(dst, src, length * sizeof(Vector3));
- } else {
- for (int i = 0; i < length; i++) {
- M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
- *raw = MARSHALLED_OUT(Vector3, src[i]);
- }
- }
-
- return ret;
-}
-
-PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) {
- PackedVector3Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- Vector3 *dst = ret.ptrw();
-
- if constexpr (InteropLayout::MATCHES_Vector3) {
- const Vector3 *src = mono_array_addr(p_array, Vector3, 0);
- memcpy(dst, src, length * sizeof(Vector3));
- } else {
- for (int i = 0; i < length; i++) {
- dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
- }
- }
-
- return ret;
-}
-
-Callable managed_to_callable(const M_Callable &p_managed_callable) {
- if (p_managed_callable.delegate) {
- // TODO: Use pooling for ManagedCallable instances.
- CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate));
- return Callable(managed_callable);
- } else {
- Object *target = p_managed_callable.target
- ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target))
- : nullptr;
- StringName *method_ptr = p_managed_callable.method_string_name
- ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name))
- : nullptr;
- StringName method = method_ptr ? *method_ptr : StringName();
- return Callable(target, method);
- }
-}
-
-M_Callable callable_to_managed(const Callable &p_callable) {
- if (p_callable.is_custom()) {
- CallableCustom *custom = p_callable.get_custom();
- CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
-
- if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
- ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
- return {
- nullptr, nullptr,
- managed_callable->get_delegate()
- };
- } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
- SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
- return {
- GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(signal_awaiter_callable->get_object())),
- GDMonoUtils::create_managed_from(signal_awaiter_callable->get_signal()),
- nullptr
- };
- } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
- EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
- return {
- GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(event_signal_callable->get_object())),
- GDMonoUtils::create_managed_from(event_signal_callable->get_signal()),
- nullptr
- };
- }
-
- // Some other CallableCustom. We only support ManagedCallable.
- return { nullptr, nullptr, nullptr };
- } else {
- MonoObject *target_managed = GDMonoUtils::unmanaged_get_managed(p_callable.get_object());
- MonoObject *method_string_name_managed = GDMonoUtils::create_managed_from(p_callable.get_method());
- return { target_managed, method_string_name_managed, nullptr };
- }
-}
-
-Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) {
- Object *owner = p_managed_signal.owner
- ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner))
- : nullptr;
- StringName *name_ptr = p_managed_signal.name_string_name
- ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name))
- : nullptr;
- StringName name = name_ptr ? *name_ptr : StringName();
- return Signal(owner, name);
-}
-
-M_SignalInfo signal_info_to_managed(const Signal &p_signal) {
- Object *owner = p_signal.get_object();
- MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner);
- MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name());
- return { owner_managed, name_string_name_managed };
-}
-} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
deleted file mode 100644
index 51f11ab18a..0000000000
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ /dev/null
@@ -1,605 +0,0 @@
-/*************************************************************************/
-/* gd_mono_marshal.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 GD_MONO_MARSHAL_H
-#define GD_MONO_MARSHAL_H
-
-#include "core/variant/variant.h"
-
-#include "../managed_callable.h"
-#include "gd_mono.h"
-#include "gd_mono_utils.h"
-
-namespace GDMonoMarshal {
-
-template <typename T>
-T unbox(MonoObject *p_obj) {
- return *(T *)mono_object_unbox(p_obj);
-}
-
-template <typename T>
-T *unbox_addr(MonoObject *p_obj) {
- return (T *)mono_object_unbox(p_obj);
-}
-
-#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x)
-#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x)
-#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x)
-#define BOX_INT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int32_t), &x)
-#define BOX_INT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int16_t), &x)
-#define BOX_INT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int8_t), &x)
-#define BOX_UINT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint64_t), &x)
-#define BOX_UINT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint32_t), &x)
-#define BOX_UINT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint16_t), &x)
-#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x)
-#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x)
-#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
-#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
-
-Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = nullptr);
-
-bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
-
-// String
-
-_FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) {
- char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string);
- String ret = String(utf32);
- mono_free(utf32);
- return ret;
-}
-
-_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
- if (p_mono_string == nullptr) {
- return String();
- }
-
- return mono_string_to_godot_not_null(p_mono_string);
-}
-
-_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
- return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data()));
-}
-
-// 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(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(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);
-
-/// Tries to convert the MonoObject* to Variant and then convert the Variant to String.
-/// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead.
-String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc);
-
-// System.Collections.Generic
-
-MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
-Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
-
-MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
-Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
-
-// Array
-
-MonoArray *Array_to_mono_array(const Array &p_array);
-MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class);
-Array mono_array_to_Array(MonoArray *p_array);
-
-// PackedInt32Array
-
-MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array);
-PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array);
-
-// PackedInt64Array
-
-MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array);
-PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array);
-
-// PackedByteArray
-
-MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array);
-PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array);
-
-// PackedFloat32Array
-
-MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array);
-PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array);
-
-// PackedFloat64Array
-
-MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array);
-PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array);
-
-// PackedStringArray
-
-MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array);
-PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array);
-
-// PackedColorArray
-
-MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array);
-PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array);
-
-// PackedVector2Array
-
-MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array);
-PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array);
-
-// PackedVector3Array
-
-MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array);
-PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array);
-
-#pragma pack(push, 1)
-
-struct M_Callable {
- MonoObject *target = nullptr;
- MonoObject *method_string_name = nullptr;
- MonoDelegate *delegate = nullptr;
-};
-
-struct M_SignalInfo {
- MonoObject *owner = nullptr;
- MonoObject *name_string_name = nullptr;
-};
-
-#pragma pack(pop)
-
-// Callable
-Callable managed_to_callable(const M_Callable &p_managed_callable);
-M_Callable callable_to_managed(const Callable &p_callable);
-
-// SignalInfo
-Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal);
-M_SignalInfo signal_info_to_managed(const Signal &p_signal);
-
-// Structures
-
-namespace InteropLayout {
-
-enum {
- MATCHES_int = (sizeof(int32_t) == sizeof(uint32_t)),
-
- MATCHES_float = (sizeof(float) == sizeof(uint32_t)),
-
- MATCHES_double = (sizeof(double) == sizeof(uint64_t)),
-
-#ifdef REAL_T_IS_DOUBLE
- MATCHES_real_t = (sizeof(real_t) == sizeof(uint64_t)),
-#else
- MATCHES_real_t = (sizeof(real_t) == sizeof(uint32_t)),
-#endif
-
- MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) &&
- offsetof(Vector2, x) == (sizeof(real_t) * 0) &&
- offsetof(Vector2, y) == (sizeof(real_t) * 1)),
-
- MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) &&
- offsetof(Vector2i, x) == (sizeof(int32_t) * 0) &&
- offsetof(Vector2i, y) == (sizeof(int32_t) * 1)),
-
- MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) &&
- offsetof(Rect2, position) == (sizeof(Vector2) * 0) &&
- offsetof(Rect2, size) == (sizeof(Vector2) * 1)),
-
- MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) &&
- offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) &&
- offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)),
-
- MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array
-
- MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) &&
- offsetof(Vector3, x) == (sizeof(real_t) * 0) &&
- offsetof(Vector3, y) == (sizeof(real_t) * 1) &&
- offsetof(Vector3, z) == (sizeof(real_t) * 2)),
-
- MATCHES_Vector4 = (MATCHES_real_t && (sizeof(Vector4) == (sizeof(real_t) * 4)) &&
- offsetof(Vector4, x) == (sizeof(real_t) * 0) &&
- offsetof(Vector4, y) == (sizeof(real_t) * 1) &&
- offsetof(Vector4, z) == (sizeof(real_t) * 2) &&
- offsetof(Vector4, w) == (sizeof(real_t) * 3)),
-
- MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4)) &&
- offsetof(Vector4i, x) == (sizeof(int32_t) * 0) &&
- offsetof(Vector4i, y) == (sizeof(int32_t) * 1) &&
- offsetof(Vector4i, z) == (sizeof(int32_t) * 2) &&
- offsetof(Vector4i, w) == (sizeof(int32_t) * 3)),
-
- MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) &&
- offsetof(Vector3i, x) == (sizeof(int32_t) * 0) &&
- offsetof(Vector3i, y) == (sizeof(int32_t) * 1) &&
- offsetof(Vector3i, z) == (sizeof(int32_t) * 2)),
-
- MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array
-
- MATCHES_Quaternion = (MATCHES_real_t && (sizeof(Quaternion) == (sizeof(real_t) * 4)) &&
- offsetof(Quaternion, x) == (sizeof(real_t) * 0) &&
- offsetof(Quaternion, y) == (sizeof(real_t) * 1) &&
- offsetof(Quaternion, z) == (sizeof(real_t) * 2) &&
- offsetof(Quaternion, w) == (sizeof(real_t) * 3)),
-
- MATCHES_Transform3D = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform3D) == (sizeof(Basis) + sizeof(Vector3))) &&
- offsetof(Transform3D, basis) == 0 &&
- offsetof(Transform3D, origin) == sizeof(Basis)),
-
- MATCHES_Projection = (MATCHES_Vector4 && (sizeof(Projection) == (sizeof(Vector4) * 4))),
-
- MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) &&
- offsetof(AABB, position) == (sizeof(Vector3) * 0) &&
- offsetof(AABB, size) == (sizeof(Vector3) * 1)),
-
- MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) &&
- offsetof(Color, r) == (sizeof(float) * 0) &&
- offsetof(Color, g) == (sizeof(float) * 1) &&
- offsetof(Color, b) == (sizeof(float) * 2) &&
- offsetof(Color, a) == (sizeof(float) * 3)),
-
- MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) &&
- offsetof(Plane, normal) == 0 &&
- offsetof(Plane, d) == sizeof(Vector3))
-};
-
-// In the future we may force this if we want to ref return these structs
-#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
-/* clang-format off */
-static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && MATCHES_Vector4 &&
- MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_Projection && MATCHES_AABB && MATCHES_Color &&
- MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i && MATCHES_Vector4i);
-/* clang-format on */
-#endif
-} // namespace InteropLayout
-
-#pragma pack(push, 1)
-
-struct M_Vector2 {
- real_t x, y;
-
- static _FORCE_INLINE_ Vector2 convert_to(const M_Vector2 &p_from) {
- return Vector2(p_from.x, p_from.y);
- }
-
- static _FORCE_INLINE_ M_Vector2 convert_from(const Vector2 &p_from) {
- M_Vector2 ret = { p_from.x, p_from.y };
- return ret;
- }
-};
-
-struct M_Vector2i {
- int32_t x, y;
-
- static _FORCE_INLINE_ Vector2i convert_to(const M_Vector2i &p_from) {
- return Vector2i(p_from.x, p_from.y);
- }
-
- static _FORCE_INLINE_ M_Vector2i convert_from(const Vector2i &p_from) {
- M_Vector2i ret = { p_from.x, p_from.y };
- return ret;
- }
-};
-
-struct M_Rect2 {
- M_Vector2 position;
- M_Vector2 size;
-
- static _FORCE_INLINE_ Rect2 convert_to(const M_Rect2 &p_from) {
- return Rect2(M_Vector2::convert_to(p_from.position),
- M_Vector2::convert_to(p_from.size));
- }
-
- static _FORCE_INLINE_ M_Rect2 convert_from(const Rect2 &p_from) {
- M_Rect2 ret = { M_Vector2::convert_from(p_from.position), M_Vector2::convert_from(p_from.size) };
- return ret;
- }
-};
-
-struct M_Rect2i {
- M_Vector2i position;
- M_Vector2i size;
-
- static _FORCE_INLINE_ Rect2i convert_to(const M_Rect2i &p_from) {
- return Rect2i(M_Vector2i::convert_to(p_from.position),
- M_Vector2i::convert_to(p_from.size));
- }
-
- static _FORCE_INLINE_ M_Rect2i convert_from(const Rect2i &p_from) {
- M_Rect2i ret = { M_Vector2i::convert_from(p_from.position), M_Vector2i::convert_from(p_from.size) };
- return ret;
- }
-};
-
-struct M_Transform2D {
- M_Vector2 elements[3];
-
- static _FORCE_INLINE_ Transform2D convert_to(const M_Transform2D &p_from) {
- return Transform2D(p_from.elements[0].x, p_from.elements[0].y,
- p_from.elements[1].x, p_from.elements[1].y,
- p_from.elements[2].x, p_from.elements[2].y);
- }
-
- static _FORCE_INLINE_ M_Transform2D convert_from(const Transform2D &p_from) {
- M_Transform2D ret = {
- M_Vector2::convert_from(p_from.columns[0]),
- M_Vector2::convert_from(p_from.columns[1]),
- M_Vector2::convert_from(p_from.columns[2])
- };
- return ret;
- }
-};
-
-struct M_Vector3 {
- real_t x, y, z;
-
- static _FORCE_INLINE_ Vector3 convert_to(const M_Vector3 &p_from) {
- return Vector3(p_from.x, p_from.y, p_from.z);
- }
-
- static _FORCE_INLINE_ M_Vector3 convert_from(const Vector3 &p_from) {
- M_Vector3 ret = { p_from.x, p_from.y, p_from.z };
- return ret;
- }
-};
-
-struct M_Vector3i {
- int32_t x, y, z;
-
- static _FORCE_INLINE_ Vector3i convert_to(const M_Vector3i &p_from) {
- return Vector3i(p_from.x, p_from.y, p_from.z);
- }
-
- static _FORCE_INLINE_ M_Vector3i convert_from(const Vector3i &p_from) {
- M_Vector3i ret = { p_from.x, p_from.y, p_from.z };
- return ret;
- }
-};
-
-struct M_Vector4 {
- real_t x, y, z, w;
-
- static _FORCE_INLINE_ Vector4 convert_to(const M_Vector4 &p_from) {
- return Vector4(p_from.x, p_from.y, p_from.z, p_from.w);
- }
-
- static _FORCE_INLINE_ M_Vector4 convert_from(const Vector4 &p_from) {
- M_Vector4 ret = { p_from.x, p_from.y, p_from.z, p_from.w };
- return ret;
- }
-};
-
-struct M_Vector4i {
- int32_t x, y, z, w;
-
- static _FORCE_INLINE_ Vector4i convert_to(const M_Vector4i &p_from) {
- return Vector4i(p_from.x, p_from.y, p_from.z, p_from.w);
- }
-
- static _FORCE_INLINE_ M_Vector4i convert_from(const Vector4i &p_from) {
- M_Vector4i ret = { p_from.x, p_from.y, p_from.z, p_from.w };
- return ret;
- }
-};
-
-struct M_Basis {
- M_Vector3 elements[3];
-
- static _FORCE_INLINE_ Basis convert_to(const M_Basis &p_from) {
- return Basis(M_Vector3::convert_to(p_from.elements[0]),
- M_Vector3::convert_to(p_from.elements[1]),
- M_Vector3::convert_to(p_from.elements[2]));
- }
-
- static _FORCE_INLINE_ M_Basis convert_from(const Basis &p_from) {
- M_Basis ret = {
- M_Vector3::convert_from(p_from.rows[0]),
- M_Vector3::convert_from(p_from.rows[1]),
- M_Vector3::convert_from(p_from.rows[2])
- };
- return ret;
- }
-};
-
-struct M_Quaternion {
- real_t x, y, z, w;
-
- static _FORCE_INLINE_ Quaternion convert_to(const M_Quaternion &p_from) {
- return Quaternion(p_from.x, p_from.y, p_from.z, p_from.w);
- }
-
- static _FORCE_INLINE_ M_Quaternion convert_from(const Quaternion &p_from) {
- M_Quaternion ret = { p_from.x, p_from.y, p_from.z, p_from.w };
- return ret;
- }
-};
-
-struct M_Transform3D {
- M_Basis basis;
- M_Vector3 origin;
-
- static _FORCE_INLINE_ Transform3D convert_to(const M_Transform3D &p_from) {
- return Transform3D(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin));
- }
-
- static _FORCE_INLINE_ M_Transform3D convert_from(const Transform3D &p_from) {
- M_Transform3D ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) };
- return ret;
- }
-};
-
-struct M_Projection {
- M_Vector4 vec1;
- M_Vector4 vec2;
- M_Vector4 vec3;
- M_Vector4 vec4;
-
- static _FORCE_INLINE_ Projection convert_to(const M_Projection &p_from) {
- return Projection(M_Vector4::convert_to(p_from.vec1), M_Vector4::convert_to(p_from.vec2), M_Vector4::convert_to(p_from.vec3), M_Vector4::convert_to(p_from.vec4));
- }
-
- static _FORCE_INLINE_ M_Projection convert_from(const Projection &p_from) {
- M_Projection ret = { M_Vector4::convert_from(p_from.matrix[0]), M_Vector4::convert_from(p_from.matrix[1]), M_Vector4::convert_from(p_from.matrix[2]), M_Vector4::convert_from(p_from.matrix[3]) };
- return ret;
- }
-};
-
-struct M_AABB {
- M_Vector3 position;
- M_Vector3 size;
-
- static _FORCE_INLINE_ AABB convert_to(const M_AABB &p_from) {
- return AABB(M_Vector3::convert_to(p_from.position), M_Vector3::convert_to(p_from.size));
- }
-
- static _FORCE_INLINE_ M_AABB convert_from(const AABB &p_from) {
- M_AABB ret = { M_Vector3::convert_from(p_from.position), M_Vector3::convert_from(p_from.size) };
- return ret;
- }
-};
-
-struct M_Color {
- float r, g, b, a;
-
- static _FORCE_INLINE_ Color convert_to(const M_Color &p_from) {
- return Color(p_from.r, p_from.g, p_from.b, p_from.a);
- }
-
- static _FORCE_INLINE_ M_Color convert_from(const Color &p_from) {
- M_Color ret = { p_from.r, p_from.g, p_from.b, p_from.a };
- return ret;
- }
-};
-
-struct M_Plane {
- M_Vector3 normal;
- real_t d;
-
- static _FORCE_INLINE_ Plane convert_to(const M_Plane &p_from) {
- return Plane(M_Vector3::convert_to(p_from.normal), p_from.d);
- }
-
- static _FORCE_INLINE_ M_Plane convert_from(const Plane &p_from) {
- M_Plane ret = { M_Vector3::convert_from(p_from.normal), p_from.d };
- return ret;
- }
-};
-
-#pragma pack(pop)
-
-#define DECL_TYPE_MARSHAL_TEMPLATES(m_type) \
- template <int> \
- _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl(const M_##m_type *p_from); \
- \
- template <> \
- _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<0>(const M_##m_type *p_from) { \
- return M_##m_type::convert_to(*p_from); \
- } \
- \
- template <> \
- _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<1>(const M_##m_type *p_from) { \
- return *reinterpret_cast<const m_type *>(p_from); \
- } \
- \
- _FORCE_INLINE_ m_type marshalled_in_##m_type(const M_##m_type *p_from) { \
- return marshalled_in_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \
- } \
- \
- template <int> \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl(const m_type &p_from); \
- \
- template <> \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<0>(const m_type &p_from) { \
- return M_##m_type::convert_from(p_from); \
- } \
- \
- template <> \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<1>(const m_type &p_from) { \
- return *reinterpret_cast<const M_##m_type *>(&p_from); \
- } \
- \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type(const m_type &p_from) { \
- return marshalled_out_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \
- }
-
-DECL_TYPE_MARSHAL_TEMPLATES(Vector2)
-DECL_TYPE_MARSHAL_TEMPLATES(Vector2i)
-DECL_TYPE_MARSHAL_TEMPLATES(Rect2)
-DECL_TYPE_MARSHAL_TEMPLATES(Rect2i)
-DECL_TYPE_MARSHAL_TEMPLATES(Transform2D)
-DECL_TYPE_MARSHAL_TEMPLATES(Vector3)
-DECL_TYPE_MARSHAL_TEMPLATES(Vector3i)
-DECL_TYPE_MARSHAL_TEMPLATES(Basis)
-DECL_TYPE_MARSHAL_TEMPLATES(Vector4)
-DECL_TYPE_MARSHAL_TEMPLATES(Vector4i)
-DECL_TYPE_MARSHAL_TEMPLATES(Quaternion)
-DECL_TYPE_MARSHAL_TEMPLATES(Transform3D)
-DECL_TYPE_MARSHAL_TEMPLATES(Projection)
-DECL_TYPE_MARSHAL_TEMPLATES(AABB)
-DECL_TYPE_MARSHAL_TEMPLATES(Color)
-DECL_TYPE_MARSHAL_TEMPLATES(Plane)
-
-#define MARSHALLED_IN(m_type, m_from_ptr) (GDMonoMarshal::marshalled_in_##m_type(m_from_ptr))
-#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from))
-} // namespace GDMonoMarshal
-
-#endif // GD_MONO_MARSHAL_H
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
deleted file mode 100644
index 6734b44783..0000000000
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-/*************************************************************************/
-/* gd_mono_method.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 "gd_mono_method.h"
-
-#include <mono/metadata/attrdefs.h>
-#include <mono/metadata/debug-helpers.h>
-
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-void GDMonoMethod::_update_signature() {
- // Apparently MonoMethodSignature needs not to be freed.
- // mono_method_signature caches the result, we don't need to cache it ourselves.
-
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
- _update_signature(method_sig);
-}
-
-void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
- params_count = mono_signature_get_param_count(p_method_sig);
-
- MonoType *ret_type = mono_signature_get_return_type(p_method_sig);
- if (ret_type) {
- return_type.type_encoding = mono_type_get_type(ret_type);
-
- if (return_type.type_encoding != MONO_TYPE_VOID) {
- MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
- return_type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
- }
- }
-
- void *iter = nullptr;
- MonoType *param_raw_type;
- while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != nullptr) {
- ManagedType param_type;
-
- param_type.type_encoding = mono_type_get_type(param_raw_type);
-
- MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
- param_type.type_class = GDMono::get_singleton()->get_class(param_type_class);
-
- param_types.push_back(param_type);
- }
-
- // 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 {
- return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method));
-}
-
-bool GDMonoMethod::is_static() {
- return mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_STATIC;
-}
-
-IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
- switch (mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
- case MONO_METHOD_ATTR_PRIVATE:
- return IMonoClassMember::PRIVATE;
- case MONO_METHOD_ATTR_FAM_AND_ASSEM:
- return IMonoClassMember::PROTECTED_AND_INTERNAL;
- case MONO_METHOD_ATTR_ASSEM:
- return IMonoClassMember::INTERNAL;
- case MONO_METHOD_ATTR_FAMILY:
- return IMonoClassMember::PROTECTED;
- case MONO_METHOD_ATTR_PUBLIC:
- return IMonoClassMember::PUBLIC;
- default:
- ERR_FAIL_V(IMonoClassMember::PRIVATE);
- }
-}
-
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const {
- MonoException *exc = nullptr;
- 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;
-
- for (int i = 0; i < params_count; i++) {
- params[i] = GDMonoMarshal::variant_to_managed_unboxed(p_params[i], param_types[i], buffer + offset, offset);
- }
-
- ret = GDMonoUtils::runtime_invoke(mono_method, p_object, params, &exc);
- } else {
- ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc);
- }
-
- if (exc) {
- ret = nullptr;
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-
- return ret;
-}
-
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const {
- ERR_FAIL_COND_V(get_parameters_count() > 0, nullptr);
- return invoke_raw(p_object, nullptr, r_exc);
-}
-
-MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const {
- MonoException *exc = nullptr;
- MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
-
- if (exc) {
- ret = nullptr;
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-
- return ret;
-}
-
-bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, false);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoMethod::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
- attributes = mono_custom_attrs_from_method(mono_method);
- attrs_fetched = true;
-}
-
-String GDMonoMethod::get_full_name(bool p_signature) const {
- char *res = mono_method_full_name(mono_method, p_signature);
- String full_name(res);
- mono_free(res);
- return full_name;
-}
-
-String GDMonoMethod::get_full_name_no_class() const {
- String res;
-
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
-
- char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
- res += ret_str;
- mono_free(ret_str);
-
- res += " ";
- res += name;
- res += "(";
-
- char *sig_desc = mono_signature_get_desc(method_sig, true);
- res += sig_desc;
- mono_free(sig_desc);
-
- res += ")";
-
- return res;
-}
-
-String GDMonoMethod::get_ret_type_full_name() const {
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
- char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
- String res = ret_str;
- mono_free(ret_str);
- return res;
-}
-
-String GDMonoMethod::get_signature_desc(bool p_namespaces) const {
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
- char *sig_desc = mono_signature_get_desc(method_sig, p_namespaces);
- String res = sig_desc;
- mono_free(sig_desc);
- return res;
-}
-
-void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
- if (params_count > 0) {
- const char **_names = memnew_arr(const char *, params_count);
- mono_method_get_param_names(mono_method, _names);
- for (int i = 0; i < params_count; ++i) {
- names.push_back(StringName(_names[i]));
- }
- memdelete_arr(_names);
- }
-}
-
-void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const {
- for (int i = 0; i < params_count; ++i) {
- types.push_back(param_types[i]);
- }
-}
-
-const MethodInfo &GDMonoMethod::get_method_info() {
- if (!method_info_fetched) {
- 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;
- }
-
- Vector<StringName> names;
- get_parameter_names(names);
-
- for (int i = 0; i < params_count; ++i) {
- nil_is_variant = false;
- PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]);
- if (arg_info.type == Variant::NIL && nil_is_variant) {
- arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
-
- method_info.arguments.push_back(arg_info);
- }
-
- // TODO: default arguments
-
- method_info_fetched = true;
- }
-
- return method_info;
-}
-
-GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) :
- name(p_name), mono_method(p_method) {
- _update_signature();
-}
-
-GDMonoMethod::~GDMonoMethod() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
deleted file mode 100644
index be11ef5bfe..0000000000
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*************************************************************************/
-/* gd_mono_method.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 GD_MONO_METHOD_H
-#define GD_MONO_METHOD_H
-
-#include "gd_mono.h"
-#include "gd_mono_header.h"
-#include "i_mono_class_member.h"
-
-class GDMonoMethod : public IMonoClassMember {
- StringName name;
-
- uint16_t params_count;
- unsigned int params_buffer_size = 0;
- ManagedType return_type;
- Vector<ManagedType> param_types;
-
- bool method_info_fetched = false;
- MethodInfo method_info;
-
- bool attrs_fetched = false;
- MonoCustomAttrInfo *attributes = nullptr;
-
- void _update_signature();
- void _update_signature(MonoMethodSignature *p_method_sig);
-
- friend class GDMonoClass;
-
- MonoMethod *mono_method = nullptr;
-
-public:
- virtual GDMonoClass *get_enclosing_class() const final;
-
- virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; }
-
- virtual StringName get_name() const final { return name; }
-
- virtual bool is_static() final;
-
- virtual Visibility get_visibility() final;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) final;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
- void fetch_attributes();
-
- _FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
-
- _FORCE_INLINE_ uint16_t get_parameters_count() const { return params_count; }
- _FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
-
- MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const;
- MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = nullptr) const;
- MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr) const;
-
- String get_full_name(bool p_signature = false) const;
- String get_full_name_no_class() const;
- String get_ret_type_full_name() const;
- String get_signature_desc(bool p_namespaces = false) const;
-
- void get_parameter_names(Vector<StringName> &names) const;
- void get_parameter_types(Vector<ManagedType> &types) const;
-
- const MethodInfo &get_method_info();
-
- GDMonoMethod(StringName p_name, MonoMethod *p_method);
- ~GDMonoMethod();
-};
-
-#endif // GD_MONO_METHOD_H
diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h
deleted file mode 100644
index 0180dee3ea..0000000000
--- a/modules/mono/mono_gd/gd_mono_method_thunk.h
+++ /dev/null
@@ -1,320 +0,0 @@
-/*************************************************************************/
-/* gd_mono_method_thunk.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 GD_MONO_METHOD_THUNK_H
-#define GD_MONO_METHOD_THUNK_H
-
-#include <type_traits>
-
-#include "gd_mono_class.h"
-#include "gd_mono_header.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_method.h"
-#include "gd_mono_utils.h"
-
-#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED)
-#define HAVE_METHOD_THUNKS
-#endif
-
-#ifdef HAVE_METHOD_THUNKS
-
-template <class... ParamTypes>
-struct GDMonoMethodThunk {
- typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
-
- M mono_method_thunk = nullptr;
-
-public:
- _FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_method_thunk(p_args..., r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method_thunk == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method_thunk = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
- }
-
- GDMonoMethodThunk() {}
-
- explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
- set_from_method(p_mono_method);
- }
-};
-
-template <class R, class... ParamTypes>
-struct GDMonoMethodThunkR {
- typedef R(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
-
- M mono_method_thunk = nullptr;
-
-public:
- _FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- R r = mono_method_thunk(p_args..., r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return r;
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method_thunk == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method_thunk = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
- }
-
- GDMonoMethodThunkR() {}
-
- explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
-#endif
- mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
- }
-};
-
-#else
-
-template <unsigned int ThunkParamCount, class P1, class... ParamTypes>
-struct VariadicInvokeMonoMethodImpl {
- static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[ThunkParamCount] = { p_arg1, p_args... };
- p_mono_method->invoke_raw(nullptr, args, r_exc);
- } else {
- void *args[ThunkParamCount] = { p_args... };
- p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
- }
- }
-};
-
-template <unsigned int ThunkParamCount, class... ParamTypes>
-struct VariadicInvokeMonoMethod {
- static void invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
- VariadicInvokeMonoMethodImpl<ThunkParamCount, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
- }
-};
-
-template <>
-struct VariadicInvokeMonoMethod<0> {
- static void invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!p_mono_method->is_static());
-#endif
- p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
- }
-};
-
-template <class P1>
-struct VariadicInvokeMonoMethod<1, P1> {
- static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[1] = { p_arg1 };
- p_mono_method->invoke_raw(nullptr, args, r_exc);
- } else {
- p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
- }
- }
-};
-
-template <class R>
-R unbox_if_needed(MonoObject *p_val, const ManagedType &, typename std::enable_if<!std::is_pointer<R>::value>::type * = 0) {
- return GDMonoMarshal::unbox<R>(p_val);
-}
-
-template <class R>
-R unbox_if_needed(MonoObject *p_val, const ManagedType &p_type, typename std::enable_if<std::is_pointer<R>::value>::type * = 0) {
- if (mono_class_is_valuetype(p_type.type_class->get_mono_ptr())) {
- return GDMonoMarshal::unbox<R>(p_val);
- } else {
- // If it's not a value type, we assume 'R' is a pointer to 'MonoObject' or a compatible type, like 'MonoException'.
- return (R)p_val;
- }
-}
-
-template <unsigned int ThunkParamCount, class R, class P1, class... ParamTypes>
-struct VariadicInvokeMonoMethodRImpl {
- static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[ThunkParamCount] = { p_arg1, p_args... };
- MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- } else {
- void *args[ThunkParamCount] = { p_args... };
- MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- }
- }
-};
-
-template <unsigned int ThunkParamCount, class R, class... ParamTypes>
-struct VariadicInvokeMonoMethodR {
- static R invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
- return VariadicInvokeMonoMethodRImpl<ThunkParamCount, R, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
- }
-};
-
-template <class R>
-struct VariadicInvokeMonoMethodR<0, R> {
- static R invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!p_mono_method->is_static());
-#endif
- MonoObject *r = p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- }
-};
-
-template <class R, class P1>
-struct VariadicInvokeMonoMethodR<1, R, P1> {
- static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[1] = { p_arg1 };
- MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- } else {
- MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- }
- }
-};
-
-template <class... ParamTypes>
-struct GDMonoMethodThunk {
- GDMonoMethod *mono_method = nullptr;
-
-public:
- _FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
- VariadicInvokeMonoMethod<sizeof...(ParamTypes), ParamTypes...>::invoke(mono_method, p_args..., r_exc);
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method = p_mono_method;
- }
-
- GDMonoMethodThunk() {}
-
- explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
- set_from_method(p_mono_method);
- }
-};
-
-template <class R, class... ParamTypes>
-struct GDMonoMethodThunkR {
- GDMonoMethod *mono_method = nullptr;
-
-public:
- _FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
- return VariadicInvokeMonoMethodR<sizeof...(ParamTypes), R, ParamTypes...>::invoke(mono_method, p_args..., r_exc);
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method = p_mono_method;
- }
-
- GDMonoMethodThunkR() {}
-
- explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
- set_from_method(p_mono_method);
- }
-};
-
-#endif
-
-#endif // GD_MONO_METHOD_THUNK_H
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
deleted file mode 100644
index c9775ae9cb..0000000000
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*************************************************************************/
-/* gd_mono_property.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 "gd_mono_property.h"
-
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-#include <mono/metadata/attrdefs.h>
-
-GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner) {
- owner = p_owner;
- mono_property = p_mono_property;
- name = String::utf8(mono_property_get_name(mono_property));
-
- MonoMethod *prop_method = mono_property_get_get_method(mono_property);
-
- if (prop_method) {
- MonoMethodSignature *getter_sig = mono_method_signature(prop_method);
-
- MonoType *ret_type = mono_signature_get_return_type(getter_sig);
-
- type.type_encoding = mono_type_get_type(ret_type);
- MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
- type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
- } else {
- prop_method = mono_property_get_set_method(mono_property);
-
- MonoMethodSignature *setter_sig = mono_method_signature(prop_method);
-
- void *iter = nullptr;
- MonoType *param_raw_type = mono_signature_get_params(setter_sig, &iter);
-
- type.type_encoding = mono_type_get_type(param_raw_type);
- MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
- 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;
-}
-
-GDMonoProperty::~GDMonoProperty() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-}
-
-bool GDMonoProperty::is_static() {
- MonoMethod *prop_method = mono_property_get_get_method(mono_property);
- if (prop_method == nullptr) {
- prop_method = mono_property_get_set_method(mono_property);
- }
- return mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_STATIC;
-}
-
-IMonoClassMember::Visibility GDMonoProperty::get_visibility() {
- MonoMethod *prop_method = mono_property_get_get_method(mono_property);
- if (prop_method == nullptr) {
- prop_method = mono_property_get_set_method(mono_property);
- }
-
- switch (mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
- case MONO_METHOD_ATTR_PRIVATE:
- return IMonoClassMember::PRIVATE;
- case MONO_METHOD_ATTR_FAM_AND_ASSEM:
- return IMonoClassMember::PROTECTED_AND_INTERNAL;
- case MONO_METHOD_ATTR_ASSEM:
- return IMonoClassMember::INTERNAL;
- case MONO_METHOD_ATTR_FAMILY:
- return IMonoClassMember::PROTECTED;
- case MONO_METHOD_ATTR_PUBLIC:
- return IMonoClassMember::PUBLIC;
- default:
- ERR_FAIL_V(IMonoClassMember::PRIVATE);
- }
-}
-
-bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, false);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoProperty::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoProperty::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
- attributes = mono_custom_attrs_from_property(owner->get_mono_ptr(), mono_property);
- attrs_fetched = true;
-}
-
-bool GDMonoProperty::has_getter() {
- return mono_property_get_get_method(mono_property) != nullptr;
-}
-
-bool GDMonoProperty::has_setter() {
- return mono_property_get_set_method(mono_property) != nullptr;
-}
-
-void GDMonoProperty::set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc) {
- uint8_t *buffer = (uint8_t *)alloca(param_buffer_size);
- unsigned int offset = 0;
-
- void *params[1] = {
- GDMonoMarshal::variant_to_managed_unboxed(p_value, type, buffer, offset)
- };
-
-#ifdef DEBUG_ENABLED
- CRASH_COND(offset != param_buffer_size);
-#endif
-
- MonoException *exc = nullptr;
- GDMonoUtils::property_set_value(mono_property, p_object, params, &exc);
- if (exc) {
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-}
-
-MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
- MonoException *exc = nullptr;
- MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, nullptr, &exc);
-
- if (exc) {
- ret = nullptr;
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-
- return ret;
-}
-
-bool GDMonoProperty::get_bool_value(MonoObject *p_object) {
- return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object));
-}
-
-int GDMonoProperty::get_int_value(MonoObject *p_object) {
- return GDMonoMarshal::unbox<int32_t>(get_value(p_object));
-}
-
-String GDMonoProperty::get_string_value(MonoObject *p_object) {
- MonoObject *val = get_value(p_object);
- return GDMonoMarshal::mono_string_to_godot((MonoString *)val);
-}
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
deleted file mode 100644
index 6fc681aeb5..0000000000
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*************************************************************************/
-/* gd_mono_property.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 GD_MONO_PROPERTY_H
-#define GD_MONO_PROPERTY_H
-
-#include "gd_mono.h"
-#include "gd_mono_header.h"
-#include "i_mono_class_member.h"
-
-class GDMonoProperty : public IMonoClassMember {
- GDMonoClass *owner = nullptr;
- MonoProperty *mono_property = nullptr;
-
- StringName name;
- ManagedType type;
-
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes = nullptr;
-
- unsigned int param_buffer_size;
-
-public:
- virtual GDMonoClass *get_enclosing_class() const final { return owner; }
-
- virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; }
-
- virtual StringName get_name() const final { return name; }
-
- virtual bool is_static() final;
- virtual Visibility get_visibility() final;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) final;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
- void fetch_attributes();
-
- bool has_getter();
- bool has_setter();
-
- _FORCE_INLINE_ ManagedType get_type() const { return type; }
-
- void set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc = nullptr);
- MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = nullptr);
-
- bool get_bool_value(MonoObject *p_object);
- int get_int_value(MonoObject *p_object);
- String get_string_value(MonoObject *p_object);
-
- GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner);
- ~GDMonoProperty();
-};
-
-#endif // GD_MONO_PROPERTY_H
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
deleted file mode 100644
index 1983d6ebe2..0000000000
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ /dev/null
@@ -1,677 +0,0 @@
-/*************************************************************************/
-/* gd_mono_utils.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 "gd_mono_utils.h"
-
-#include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/exception.h>
-
-#include "core/debugger/engine_debugger.h"
-#include "core/debugger/script_debugger.h"
-#include "core/io/dir_access.h"
-#include "core/object/ref_counted.h"
-#include "core/os/mutex.h"
-#include "core/os/os.h"
-
-#ifdef TOOLS_ENABLED
-#include "editor/debugger/editor_debugger_node.h"
-#endif
-
-#include "../csharp_script.h"
-#include "../utils/macros.h"
-#include "gd_mono.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-
-namespace GDMonoUtils {
-
-MonoObject *unmanaged_get_managed(Object *unmanaged) {
- if (!unmanaged) {
- return nullptr;
- }
-
- if (unmanaged->get_script_instance()) {
- CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
-
- if (cs_instance) {
- return cs_instance->get_mono_object();
- }
- }
-
- // If the owner does not have a CSharpInstance...
-
- void *data = CSharpLanguage::get_instance_binding(unmanaged);
- ERR_FAIL_NULL_V(data, nullptr);
- CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
- ERR_FAIL_COND_V(!script_binding.inited, nullptr);
-
- MonoGCHandleData &gchandle = script_binding.gchandle;
-
- MonoObject *target = gchandle.get_target();
-
- if (target) {
- return target;
- }
-
- CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
-
- // Create a new one
-
-#ifdef DEBUG_ENABLED
- CRASH_COND(script_binding.type_name == StringName());
- CRASH_COND(script_binding.wrapper_class == nullptr);
-#endif
-
- MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged);
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- gchandle = MonoGCHandleData::new_strong_handle(mono_object);
-
- // Tie managed to unmanaged
- RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
-
- if (rc) {
- // 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)
- rc->reference();
- CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
- }
-
- return mono_object;
-}
-
-void set_main_thread(MonoThread *p_thread) {
- mono_thread_set_main(p_thread);
-}
-
-MonoThread *attach_current_thread() {
- ERR_FAIL_COND_V(!GDMono::get_singleton()->is_runtime_initialized(), nullptr);
- MonoDomain *scripts_domain = GDMono::get_singleton()->get_scripts_domain();
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- MonoThread *mono_thread = mono_thread_attach(scripts_domain ? scripts_domain : mono_get_root_domain());
-#else
- // The scripts domain is the root domain
- MonoThread *mono_thread = mono_thread_attach(scripts_domain);
-#endif
- ERR_FAIL_NULL_V(mono_thread, nullptr);
- return mono_thread;
-}
-
-void detach_current_thread() {
- ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
- MonoThread *mono_thread = mono_thread_current();
- ERR_FAIL_NULL(mono_thread);
- mono_thread_detach(mono_thread);
-}
-
-void detach_current_thread(MonoThread *p_mono_thread) {
- ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
- ERR_FAIL_NULL(p_mono_thread);
- mono_thread_detach(p_mono_thread);
-}
-
-MonoThread *get_current_thread() {
- return mono_thread_current();
-}
-
-bool is_thread_attached() {
- return mono_domain_get() != nullptr;
-}
-
-uint32_t new_strong_gchandle(MonoObject *p_object) {
- return mono_gchandle_new(p_object, /* pinned: */ false);
-}
-
-uint32_t new_strong_gchandle_pinned(MonoObject *p_object) {
- return mono_gchandle_new(p_object, /* pinned: */ true);
-}
-
-uint32_t new_weak_gchandle(MonoObject *p_object) {
- return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
-}
-
-void free_gchandle(uint32_t p_gchandle) {
- mono_gchandle_free(p_gchandle);
-}
-
-void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) {
- GDMonoMethod *ctor = p_class->get_method(".ctor", 0);
- ERR_FAIL_NULL(ctor);
- 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));
-}
-
-GDMonoClass *type_get_proxy_class(const StringName &p_type) {
- String class_name = p_type;
-
- if (class_name[0] == '_') {
- class_name = class_name.substr(1, class_name.length());
- }
-
- GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
-
- if (klass && klass->is_static()) {
- // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
- return GDMonoCache::cached_data.class_GodotObject;
- }
-
-#ifdef TOOLS_ENABLED
- if (!klass) {
- return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
- }
-#endif
-
- return klass;
-}
-
-GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
- GDMonoClass *klass = p_class;
-
- do {
- const GDMonoAssembly *assembly = klass->get_assembly();
-
- if (assembly == GDMono::get_singleton()->get_core_api_assembly()) {
- return klass;
- }
-#ifdef TOOLS_ENABLED
- if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) {
- return klass;
- }
-#endif
- } while ((klass = klass->get_parent_class()) != nullptr);
-
- return nullptr;
-}
-
-MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
- bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), p_native);
- ERR_FAIL_COND_V_MSG(!parent_is_object_class, nullptr,
- "Type inherits from native type '" + p_native + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'.");
-
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
-
- // Construct
- GDMonoUtils::runtime_object_init(mono_object, p_class);
-
- 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 + "'...");
-
- MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), nullptr);
-
- if (domain) {
- // Workaround to avoid this exception:
- // System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system.
- // ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null.
- mono_domain_set_config(domain, ".", "");
- }
-
- return domain;
-}
-
-String get_type_desc(MonoType *p_type) {
- return mono_type_full_name(p_type);
-}
-
-String get_type_desc(MonoReflectionType *p_reftype) {
- return get_type_desc(mono_reflection_type_get_type(p_reftype));
-}
-
-String get_exception_name_and_message(MonoException *p_exc) {
- String res;
-
- MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
- MonoType *type = mono_class_get_type(klass);
-
- char *full_name = mono_type_full_name(type);
- res += full_name;
- mono_free(full_name);
-
- res += ": ";
-
- MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, nullptr, nullptr);
- res += GDMonoMarshal::mono_string_to_godot(msg);
-
- return res;
-}
-
-void debug_print_unhandled_exception(MonoException *p_exc) {
- print_unhandled_exception(p_exc);
- debug_send_unhandled_exception_error(p_exc);
-}
-
-void debug_send_unhandled_exception_error(MonoException *p_exc) {
-#ifdef DEBUG_ENABLED
- if (!EngineDebugger::is_active()) {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
- }
-#endif
- return;
- }
-
- static thread_local bool _recursion_flag_ = false;
- if (_recursion_flag_) {
- return;
- }
- _recursion_flag_ = true;
- SCOPE_EXIT { _recursion_flag_ = false; };
-
- ScriptLanguage::StackInfo separator;
- separator.file = String();
- separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
- separator.line = 0;
-
- Vector<ScriptLanguage::StackInfo> si;
- String exc_msg;
-
- while (p_exc != nullptr) {
- GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
- MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
-
- MonoBoolean need_file_info = true;
- void *ctor_args[2] = { p_exc, &need_file_info };
-
- MonoException *unexpected_exc = nullptr;
- CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
-
- if (unexpected_exc) {
- GDMonoInternals::unhandled_exception(unexpected_exc);
- return;
- }
-
- Vector<ScriptLanguage::StackInfo> _si;
- if (stack_trace != nullptr) {
- _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
- for (int i = _si.size() - 1; i >= 0; i--) {
- si.insert(0, _si[i]);
- }
- }
-
- exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
-
- GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
- GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
- CRASH_COND(inner_exc_prop == nullptr);
-
- MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
- if (inner_exc != nullptr) {
- si.insert(0, separator);
- }
-
- p_exc = (MonoException *)inner_exc;
- }
-
- String file = si.size() ? si[0].file : __FILE__;
- String func = si.size() ? si[0].func : FUNCTION_STR;
- int line = si.size() ? si[0].line : __LINE__;
- String error_msg = "Unhandled exception";
-
- EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, true, ERR_HANDLER_ERROR, si);
-#endif
-}
-
-void debug_unhandled_exception(MonoException *p_exc) {
- GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
-}
-
-void print_unhandled_exception(MonoException *p_exc) {
- mono_print_unhandled_exception((MonoObject *)p_exc);
-}
-
-void set_pending_exception(MonoException *p_exc) {
-#ifdef NO_PENDING_EXCEPTIONS
- debug_unhandled_exception(p_exc);
-#else
- if (get_runtime_invoke_count() == 0) {
- debug_unhandled_exception(p_exc);
- return;
- }
-
- if (!mono_runtime_set_pending_exception(p_exc, false)) {
- ERR_PRINT("Exception thrown from managed code, but it could not be set as pending:");
- GDMonoUtils::debug_print_unhandled_exception(p_exc);
- }
-#endif
-}
-
-thread_local int current_invoke_count = 0;
-
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke(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);
- GD_MONO_END_RUNTIME_INVOKE;
- return ret;
-}
-
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
-}
-
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return ret;
-}
-
-uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error) {
- r_error = false;
- switch (mono_type_get_type(p_enum_basetype)) {
- case MONO_TYPE_BOOLEAN:
- return (bool)GDMonoMarshal::unbox<MonoBoolean>(p_boxed) ? 1 : 0;
- case MONO_TYPE_CHAR:
- return GDMonoMarshal::unbox<uint16_t>(p_boxed);
- case MONO_TYPE_U1:
- return GDMonoMarshal::unbox<uint8_t>(p_boxed);
- case MONO_TYPE_U2:
- return GDMonoMarshal::unbox<uint16_t>(p_boxed);
- case MONO_TYPE_U4:
- return GDMonoMarshal::unbox<uint32_t>(p_boxed);
- case MONO_TYPE_U8:
- return GDMonoMarshal::unbox<uint64_t>(p_boxed);
- case MONO_TYPE_I1:
- return GDMonoMarshal::unbox<int8_t>(p_boxed);
- case MONO_TYPE_I2:
- return GDMonoMarshal::unbox<int16_t>(p_boxed);
- case MONO_TYPE_I4:
- return GDMonoMarshal::unbox<int32_t>(p_boxed);
- case MONO_TYPE_I8:
- return GDMonoMarshal::unbox<int64_t>(p_boxed);
- default:
- r_error = true;
- return 0;
- }
-}
-
-void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
- CACHED_METHOD_THUNK(GodotObject, Dispose).invoke(p_mono_object, r_exc);
-}
-
-namespace Marshal {
-
-#ifdef MONO_GLUE_ENABLED
-#ifdef TOOLS_ENABLED
-#define NO_GLUE_RET(m_ret) \
- { \
- if (!GDMonoCache::cached_data.godot_api_cache_updated) \
- return m_ret; \
- }
-#else
-#define NO_GLUE_RET(m_ret) \
- {}
-#endif
-#else
-#define NO_GLUE_RET(m_ret) \
- { return m_ret; }
-#endif
-
-bool type_is_generic_array(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_dictionary(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_system_generic_list(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericList).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericDictionary).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_ienumerable(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIEnumerable).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_icollection(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericICollection).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIDictionary).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_has_flags_attribute(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- 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() {
- if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) {
- mono_thread = GDMonoUtils::attach_current_thread();
- }
-}
-
-ScopeThreadAttach::~ScopeThreadAttach() {
- if (unlikely(mono_thread)) {
- GDMonoUtils::detach_current_thread(mono_thread);
- }
-}
-
-StringName get_native_godot_class_name(GDMonoClass *p_class) {
- MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr);
- StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj));
- return ptr ? *ptr : StringName();
-}
-} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
deleted file mode 100644
index 300cacfa4b..0000000000
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/*************************************************************************/
-/* gd_mono_utils.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 GD_MONO_UTILS_H
-#define GD_MONO_UTILS_H
-
-#include <mono/metadata/threads.h>
-
-#include "../mono_gc_handle.h"
-#include "../utils/macros.h"
-#include "gd_mono_header.h"
-#ifdef JAVASCRIPT_ENABLED
-#include "gd_mono_wasm_m2n.h"
-#endif
-
-#include "core/object/class_db.h"
-#include "core/object/ref_counted.h"
-
-#define UNHANDLED_EXCEPTION(m_exc) \
- if (unlikely(m_exc != nullptr)) { \
- GDMonoUtils::debug_unhandled_exception(m_exc); \
- GD_UNREACHABLE(); \
- } else \
- ((void)0)
-
-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) {
- p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
-}
-
-/**
- * If the object has a csharp script, returns the target of the gchandle stored in the script instance
- * Otherwise returns a newly constructed MonoObject* which is attached to the object
- * Returns nullptr on error
- */
-MonoObject *unmanaged_get_managed(Object *unmanaged);
-
-void set_main_thread(MonoThread *p_thread);
-MonoThread *attach_current_thread();
-void detach_current_thread();
-void detach_current_thread(MonoThread *p_mono_thread);
-MonoThread *get_current_thread();
-bool is_thread_attached();
-
-uint32_t new_strong_gchandle(MonoObject *p_object);
-uint32_t new_strong_gchandle_pinned(MonoObject *p_object);
-uint32_t new_weak_gchandle(MonoObject *p_object);
-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);
-String get_type_desc(MonoReflectionType *p_reftype);
-
-String get_exception_name_and_message(MonoException *p_exc);
-
-void debug_print_unhandled_exception(MonoException *p_exc);
-void debug_send_unhandled_exception_error(MonoException *p_exc);
-void debug_unhandled_exception(MonoException *p_exc);
-void print_unhandled_exception(MonoException *p_exc);
-
-/**
- * Sets the exception as pending. The exception will be thrown when returning to managed code.
- * If no managed method is being invoked by the runtime, the exception will be treated as
- * an unhandled exception and the method will not return.
- */
-void set_pending_exception(MonoException *p_exc);
-
-extern thread_local int current_invoke_count;
-
-_FORCE_INLINE_ int get_runtime_invoke_count() {
- return current_invoke_count;
-}
-
-_FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
- return current_invoke_count;
-}
-
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc);
-
-MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc);
-
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
-
-uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error);
-
-void dispose(MonoObject *p_mono_object, MonoException **r_exc);
-
-struct ScopeThreadAttach {
- ScopeThreadAttach();
- ~ScopeThreadAttach();
-
-private:
- MonoThread *mono_thread = nullptr;
-};
-
-StringName get_native_godot_class_name(GDMonoClass *p_class);
-
-template <typename... P>
-void add_internal_call(const char *p_name, void (*p_func)(P...)) {
-#ifdef JAVASCRIPT_ENABLED
- GDMonoWasmM2n::ICallTrampolines<P...>::add();
-#endif
- mono_add_internal_call(p_name, (void *)p_func);
-}
-
-template <typename R, typename... P>
-void add_internal_call(const char *p_name, R (*p_func)(P...)) {
-#ifdef JAVASCRIPT_ENABLED
- GDMonoWasmM2n::ICallTrampolinesR<R, P...>::add();
-#endif
- mono_add_internal_call(p_name, (void *)p_func);
-}
-} // namespace GDMonoUtils
-
-#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
-
-#define GD_MONO_BEGIN_RUNTIME_INVOKE \
- int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
- _runtime_invoke_count_ref += 1; \
- ((void)0)
-
-#define GD_MONO_END_RUNTIME_INVOKE \
- _runtime_invoke_count_ref -= 1; \
- ((void)0)
-
-#define GD_MONO_SCOPE_THREAD_ATTACH \
- GDMonoUtils::ScopeThreadAttach __gdmono__scope__thread__attach__; \
- (void)__gdmono__scope__thread__attach__; \
- ((void)0)
-
-#ifdef DEBUG_ENABLED
-#define GD_MONO_ASSERT_THREAD_ATTACHED \
- CRASH_COND(!GDMonoUtils::is_thread_attached()); \
- ((void)0)
-#else
-#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0)
-#endif
-
-#endif // GD_MONO_UTILS_H
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
deleted file mode 100644
index dbfca2dc0c..0000000000
--- a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*************************************************************************/
-/* gd_mono_wasm_m2n.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 "gd_mono_wasm_m2n.h"
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/templates/oa_hash_map.h"
-
-typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs);
-
-// This extern function is implemented in our patched version of Mono
-MONO_API void godot_mono_register_m2n_icall_trampoline_dispatch_hook(GodotMonoM2nIcallTrampolineDispatch hook);
-
-namespace GDMonoWasmM2n {
-
-struct HashMapCookieComparator {
- static bool compare(const char *p_lhs, const char *p_rhs) {
- return strcmp(p_lhs, p_rhs) == 0;
- }
-};
-
-// The default hasher supports 'const char *' C Strings, but we need a custom comparator
-OAHashMap<const char *, TrampolineFunc, HashMapHasherDefault, HashMapCookieComparator> trampolines;
-
-void set_trampoline(const char *cookies, GDMonoWasmM2n::TrampolineFunc trampoline_func) {
- trampolines.set(cookies, trampoline_func);
-}
-
-mono_bool trampoline_dispatch_hook(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs) {
- TrampolineFunc *trampoline_func = trampolines.lookup_ptr(cookie);
-
- if (!trampoline_func) {
- return false;
- }
-
- (*trampoline_func)(target_func, margs);
- return true;
-}
-
-bool initialized = false;
-
-void lazy_initialize() {
- // Doesn't need to be thread safe
- if (!initialized) {
- initialized = true;
- godot_mono_register_m2n_icall_trampoline_dispatch_hook(&trampoline_dispatch_hook);
- }
-}
-} // namespace GDMonoWasmM2n
-
-#endif
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
deleted file mode 100644
index 83e2750e5a..0000000000
--- a/modules/mono/mono_gd/gd_mono_wasm_m2n.h
+++ /dev/null
@@ -1,263 +0,0 @@
-/*************************************************************************/
-/* gd_mono_wasm_m2n.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 GD_MONO_WASM_M2N_H
-#define GD_MONO_WASM_M2N_H
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/string/ustring.h"
-#include "core/typedefs.h"
-
-#include <mono/metadata/loader.h>
-#include <mono/utils/mono-publib.h>
-#include <stdexcept>
-#include <type_traits>
-
-extern "C" {
-
-struct Mono_InterpMethodArguments {
- size_t ilen;
- void **iargs;
- size_t flen;
- double *fargs = nullptr;
- void **retval;
- size_t is_float_ret;
- //#ifdef TARGET_WASM
- void *sig = nullptr;
- //#endif
-};
-} // extern "C"
-
-namespace GDMonoWasmM2n {
-
-template <typename T, size_t Size>
-struct array {
- T elems[Size];
-};
-
-template <typename T>
-constexpr char get_m2n_cookie_impl() {
-#define M2N_REG_COOKIE(m_type, m_cookie) \
- if constexpr (std::is_same_v<m_type, T>) { \
- return m_cookie; \
- }
-
- M2N_REG_COOKIE(MonoBoolean, 'I');
- M2N_REG_COOKIE(int8_t, 'I');
- M2N_REG_COOKIE(uint8_t, 'I');
- M2N_REG_COOKIE(int16_t, 'I');
- M2N_REG_COOKIE(uint16_t, 'I');
- M2N_REG_COOKIE(int32_t, 'I');
- M2N_REG_COOKIE(uint32_t, 'I');
- M2N_REG_COOKIE(int64_t, 'L');
- M2N_REG_COOKIE(uint64_t, 'L');
- M2N_REG_COOKIE(float, 'F');
- M2N_REG_COOKIE(double, 'D');
-
- if constexpr (std::is_pointer_v<T>) {
- if constexpr (sizeof(void *) == 4) {
- return 'I';
- } else {
- return 'L';
- }
- }
-
- if constexpr (std::is_void_v<T>) {
- return 'V';
- }
-
- return 'X';
-
-#undef M2N_REG_COOKIE
-}
-
-template <typename T>
-constexpr char get_m2n_cookie() {
- constexpr char cookie = get_m2n_cookie_impl<T>();
- static_assert(cookie != 'X', "Type not supported in internal call signature.");
- return cookie;
-}
-
-template <typename... T>
-constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies() {
- return array<const char, sizeof...(T) + 2>{ 'V', get_m2n_cookie<T>()..., '\0' };
-}
-
-template <typename R, typename... T>
-constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies_r() {
- return array<const char, sizeof...(T) + 2>{ get_m2n_cookie<R>(), get_m2n_cookie<T>()..., '\0' };
-}
-
-template <typename T>
-constexpr size_t calc_m2n_index(size_t &r_int_idx, size_t &r_float_idx) {
- constexpr char cookie = get_m2n_cookie<T>();
-
- static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
-
- if constexpr (cookie == 'I' || cookie == 'L') {
- size_t ret = r_int_idx;
- r_int_idx += cookie == 'I' ? 1 : 2;
- return ret;
- } else {
- size_t ret = r_float_idx;
- r_float_idx += cookie == 'F' ? 1 : 2;
- return ret;
- }
-}
-
-template <typename... P>
-constexpr array<size_t, sizeof...(P)> get_indices_for_type() {
- size_t int_idx = 0;
- size_t float_idx = 0;
- return array<size_t, sizeof...(P)>{ calc_m2n_index<P>(int_idx, float_idx)... };
-}
-
-constexpr size_t fidx(size_t p_x) {
- if constexpr (sizeof(void *) == 4) {
- return p_x * 2;
- } else {
- return p_x;
- }
-}
-
-template <typename T>
-T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) {
- constexpr char cookie = get_m2n_cookie<T>();
-
- static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
-
- if constexpr (cookie == 'I') {
- return (T)(size_t)p_margs->iargs[p_idx];
- } else if constexpr (cookie == 'L') {
- static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> ||
- (sizeof(void *) == 8 && std::is_pointer_v<T>),
- "Invalid type for cookie 'L'.");
-
- union {
- T l;
- struct {
- int32_t lo;
- int32_t hi;
- } pair;
- } p;
-
- p.pair.lo = (int32_t)(size_t)p_margs->iargs[p_idx];
- p.pair.hi = (int32_t)(size_t)p_margs->iargs[p_idx + 1];
-
- return p.l;
- } else if constexpr (cookie == 'F') {
- return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]);
- } else if constexpr (cookie == 'D') {
- return (T)p_margs->fargs[p_idx];
- }
-}
-
-template <typename... P, size_t... Is>
-void m2n_trampoline_with_idx_seq(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
- constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
- typedef void (*Func)(P...);
- Func func = (Func)p_target_func;
- func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
-}
-
-template <typename R, typename... P, size_t... Is>
-void m2n_trampoline_with_idx_seq_r(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
- constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
- typedef R (*Func)(P...);
- Func func = (Func)p_target_func;
- R res = func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
- *reinterpret_cast<R *>(p_margs->retval) = res;
-}
-
-inline void m2n_trampoline_with_idx_seq_0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- typedef void (*Func)();
- Func func = (Func)p_target_func;
- func();
-}
-
-template <typename R>
-void m2n_trampoline_with_idx_seq_r0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- typedef R (*Func)();
- Func func = (Func)p_target_func;
- R res = func();
- *reinterpret_cast<R *>(p_margs->retval) = res;
-}
-
-template <typename... P>
-void m2n_trampoline(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- if constexpr (sizeof...(P) == 0) {
- m2n_trampoline_with_idx_seq_0(p_target_func, p_margs);
- } else {
- m2n_trampoline_with_idx_seq<P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
- }
-}
-
-template <typename R, typename... P>
-void m2n_trampoline_r(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- if constexpr (sizeof...(P) == 0) {
- m2n_trampoline_with_idx_seq_r0<R>(p_target_func, p_margs);
- } else {
- m2n_trampoline_with_idx_seq_r<R, P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
- }
-}
-
-typedef void (*TrampolineFunc)(void *p_target_func, Mono_InterpMethodArguments *p_margs);
-
-void set_trampoline(const char *cookies, TrampolineFunc trampoline_func);
-
-void lazy_initialize();
-
-template <typename... P>
-struct ICallTrampolines {
- static constexpr auto cookies = get_m2n_cookies<P...>();
-
- static void add() {
- lazy_initialize();
- set_trampoline(cookies.elems, &m2n_trampoline<P...>);
- }
-};
-
-template <typename R, typename... P>
-struct ICallTrampolinesR {
- static constexpr auto cookies = get_m2n_cookies_r<R, P...>();
-
- static void add() {
- lazy_initialize();
- set_trampoline(cookies.elems, &m2n_trampoline_r<R, P...>);
- }
-};
-
-void initialize();
-} // namespace GDMonoWasmM2n
-
-#endif
-
-#endif // GD_MONO_WASM_M2N_H
diff --git a/modules/mono/mono_gd/i_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h
deleted file mode 100644
index 14e8ca82b9..0000000000
--- a/modules/mono/mono_gd/i_mono_class_member.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*************************************************************************/
-/* i_mono_class_member.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 I_MONO_CLASS_MEMBER_H
-#define I_MONO_CLASS_MEMBER_H
-
-#include "gd_mono_header.h"
-
-#include <mono/metadata/object.h>
-
-class IMonoClassMember {
-public:
- enum Visibility {
- PRIVATE,
- PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM
- INTERNAL, // ASSEMBLY
- PROTECTED, // FAMILY
- PUBLIC
- };
-
- enum MemberType {
- MEMBER_TYPE_FIELD,
- MEMBER_TYPE_PROPERTY,
- MEMBER_TYPE_METHOD
- };
-
- virtual ~IMonoClassMember() {}
-
- virtual GDMonoClass *get_enclosing_class() const = 0;
-
- virtual MemberType get_member_type() const = 0;
-
- virtual StringName get_name() const = 0;
-
- virtual bool is_static() = 0;
-
- virtual Visibility get_visibility() = 0;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) = 0;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) = 0;
-};
-
-#endif // I_MONO_CLASS_MEMBER_H
diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp
deleted file mode 100644
index 5860d7db1e..0000000000
--- a/modules/mono/mono_gd/managed_type.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*************************************************************************/
-/* managed_type.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 "managed_type.h"
-
-#include "gd_mono.h"
-#include "gd_mono_class.h"
-
-ManagedType ManagedType::from_class(GDMonoClass *p_class) {
- return ManagedType(mono_type_get_type(p_class->get_mono_type()), p_class);
-}
-
-ManagedType ManagedType::from_class(MonoClass *p_mono_class) {
- GDMonoClass *tclass = GDMono::get_singleton()->get_class(p_mono_class);
- ERR_FAIL_COND_V(!tclass, ManagedType());
-
- return ManagedType(mono_type_get_type(tclass->get_mono_type()), tclass);
-}
-
-ManagedType ManagedType::from_type(MonoType *p_mono_type) {
- MonoClass *mono_class = mono_class_from_mono_type(p_mono_type);
- GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_class);
- ERR_FAIL_COND_V(!tclass, ManagedType());
-
- return ManagedType(mono_type_get_type(p_mono_type), tclass);
-}
-
-ManagedType ManagedType::from_reftype(MonoReflectionType *p_mono_reftype) {
- MonoType *mono_type = mono_reflection_type_get_type(p_mono_reftype);
- return from_type(mono_type);
-}
diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h
deleted file mode 100644
index 603ff3aca1..0000000000
--- a/modules/mono/mono_gd/managed_type.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*************************************************************************/
-/* managed_type.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 MANAGED_TYPE_H
-#define MANAGED_TYPE_H
-
-#include <mono/metadata/object.h>
-
-#include "gd_mono_header.h"
-
-struct ManagedType {
- int type_encoding = 0;
- GDMonoClass *type_class = nullptr;
-
- static ManagedType from_class(GDMonoClass *p_class);
- static ManagedType from_class(MonoClass *p_mono_class);
- static ManagedType from_type(MonoType *p_mono_type);
- static ManagedType from_reftype(MonoReflectionType *p_mono_reftype);
-
- ManagedType() {}
-
- ManagedType(int p_type_encoding, GDMonoClass *p_type_class) :
- type_encoding(p_type_encoding),
- type_class(p_type_class) {
- }
-};
-
-#endif // MANAGED_TYPE_H
diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp
index 4797d5dae1..7fb983cd37 100644
--- a/modules/mono/mono_gd/support/android_support.cpp
+++ b/modules/mono/mono_gd/support/android_support.cpp
@@ -359,7 +359,7 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
jsize encodedLength = env->GetArrayLength(encoded);
- MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength);
+ MonoArray *encoded_ret = mono_array_new(mono_domain_get(), mono_get_byte_class(), encodedLength);
uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 437c4ca54a..55d2138674 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -32,16 +32,14 @@
#include "csharp_script.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"
-Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) {
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
// TODO: Use pooling for ManagedCallable instances.
- SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal));
+ MonoGCHandleData awaiter_handle(p_awaiter_handle_ptr, gdmono::GCHandleType::STRONG_HANDLE);
+ SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, awaiter_handle, p_signal));
Callable callable = Callable(awaiter_callable);
return p_source->connect(p_signal, callable, Object::CONNECT_ONESHOT);
@@ -51,7 +49,7 @@ bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const Calla
// Only called if both instances are of type SignalAwaiterCallable. Static cast is safe.
const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
- return a->awaiter_handle.handle == b->awaiter_handle.handle;
+ return a->awaiter_handle.handle.value == b->awaiter_handle.handle.value;
}
bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -92,6 +90,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();
@@ -101,38 +103,20 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va
"Resumed after await, but class instance is gone.");
#endif
- MonoArray *signal_args = nullptr;
-
- if (p_argcount > 0) {
- signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
-
- for (int i = 0; i < p_argcount; i++) {
- MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
- mono_array_setref(signal_args, i, boxed);
- }
- }
+ bool awaiter_is_null = false;
+ GDMonoCache::managed_callbacks.SignalAwaiter_SignalCallback(awaiter_handle.get_intptr(), p_arguments, p_argcount, &awaiter_is_null);
- MonoObject *awaiter = awaiter_handle.get_target();
-
- if (!awaiter) {
+ if (awaiter_is_null) {
r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
- MonoException *exc = nullptr;
- CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter, signal_args, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL();
- } else {
- r_call_error.error = Callable::CallError::CALL_OK;
- }
+ r_call_error.error = Callable::CallError::CALL_OK;
}
-SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) :
+SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal) :
target_id(p_target->get_instance_id()),
- awaiter_handle(MonoGCHandleData::new_strong_handle(p_awaiter)),
+ awaiter_handle(p_awaiter_handle),
signal(p_signal) {
}
@@ -148,7 +132,7 @@ bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const Callabl
return false;
}
- if (a->event_signal != b->event_signal) {
+ if (a->event_signal_name != b->event_signal_name) {
return false;
}
@@ -163,7 +147,7 @@ bool EventSignalCallable::compare_less(const CallableCustom *p_a, const Callable
}
uint32_t EventSignalCallable::hash() const {
- uint32_t hash = event_signal->field->get_name().hash();
+ uint32_t hash = event_signal_name.hash();
return hash_murmur3_one_64(owner->get_instance_id(), hash);
}
@@ -173,8 +157,7 @@ String EventSignalCallable::get_as_text() const {
if (script.is_valid() && script->get_path().is_resource_file()) {
class_name += "(" + script->get_path().get_file() + ")";
}
- StringName signal = event_signal->field->get_name();
- return class_name + "::EventSignalMiddleman::" + String(signal);
+ return class_name + "::EventSignalMiddleman::" + String(event_signal_name);
}
CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const {
@@ -190,39 +173,32 @@ ObjectID EventSignalCallable::get_object() const {
}
StringName EventSignalCallable::get_signal() const {
- return event_signal->field->get_name();
+ return event_signal_name;
}
void EventSignalCallable::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();
- ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count());
-
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance());
ERR_FAIL_NULL(csharp_instance);
- MonoObject *owner_managed = csharp_instance->get_mono_object();
- ERR_FAIL_NULL(owner_managed);
+ GCHandleIntPtr owner_gchandle_intptr = csharp_instance->get_gchandle_intptr();
+
+ bool awaiter_is_null = false;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_RaiseEventSignal(
+ owner_gchandle_intptr, &event_signal_name,
+ p_arguments, p_argcount, &awaiter_is_null);
- MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed);
- if (!delegate_field_value) {
- r_call_error.error = Callable::CallError::CALL_OK;
+ if (awaiter_is_null) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
- MonoException *exc = nullptr;
- event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL();
- } else {
- r_call_error.error = Callable::CallError::CALL_OK;
- }
+ r_call_error.error = Callable::CallError::CALL_OK;
}
-EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) :
+EventSignalCallable::EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name) :
owner(p_owner),
- event_signal(p_event_signal) {
+ event_signal_name(p_event_signal_name) {
}
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index 532aa3e327..a53ae56bf5 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -36,9 +36,14 @@
#include "csharp_script.h"
#include "mono_gc_handle.h"
-Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter);
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr);
-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,17 +64,17 @@ 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;
- SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal);
+ SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal);
~SignalAwaiterCallable();
};
-class EventSignalCallable : public CallableCustom {
+class EventSignalCallable : public BaseSignalCallable {
Object *owner = nullptr;
- const CSharpScript::EventSignal *event_signal;
+ StringName event_signal_name;
public:
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
@@ -87,11 +92,11 @@ 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;
- EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal);
+ EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name);
};
#endif // SIGNAL_AWAITER_UTILS_H
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
deleted file mode 100644
index 8e37e6943c..0000000000
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ /dev/null
@@ -1,242 +0,0 @@
-/*************************************************************************/
-/* mono_reg_utils.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 "mono_reg_utils.h"
-#include "core/io/dir_access.h"
-
-#ifdef WINDOWS_ENABLED
-
-#include "core/os/os.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-namespace MonoRegUtils {
-
-template <int>
-REGSAM bitness_sam_impl();
-
-template <>
-REGSAM bitness_sam_impl<4>() {
- return KEY_WOW64_64KEY;
-}
-
-template <>
-REGSAM bitness_sam_impl<8>() {
- return KEY_WOW64_32KEY;
-}
-
-REGSAM _get_bitness_sam() {
- return bitness_sam_impl<sizeof(size_t)>();
-}
-
-LONG _RegOpenKey(HKEY hKey, LPCWSTR lpSubKey, PHKEY phkResult) {
- LONG res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ, phkResult);
-
- if (res != ERROR_SUCCESS) {
- res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ | _get_bitness_sam(), phkResult);
- }
-
- return res;
-}
-
-LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) {
- Vector<WCHAR> buffer;
- buffer.resize(512);
- DWORD dwBufferSize = buffer.size();
-
- LONG res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
-
- if (res == ERROR_MORE_DATA) {
- // dwBufferSize now contains the actual size
- buffer.resize(dwBufferSize);
- res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
- }
-
- if (res == ERROR_SUCCESS) {
- r_value = String(buffer.ptr(), buffer.size());
- } else {
- r_value = String();
- }
-
- return res;
-}
-
-LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) {
- HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- if (!p_old_reg) {
- res = _RegKeyQueryString(hKey, "Version", r_info.version);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
- }
-
- res = _RegKeyQueryString(hKey, "SdkInstallRoot", r_info.install_root_dir);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "FrameworkAssemblyDirectory", r_info.assembly_dir);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "MonoConfigDir", r_info.config_dir);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- if (r_info.install_root_dir.ends_with("\\")) {
- r_info.bin_dir = r_info.install_root_dir + "bin";
- } else {
- r_info.bin_dir = r_info.install_root_dir + "\\bin";
- }
-
-cleanup:
- RegCloseKey(hKey);
- return res;
-}
-
-LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) {
- String default_clr;
-
- HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "DefaultCLR", default_clr);
-
- if (res == ERROR_SUCCESS && default_clr.length()) {
- r_info.version = default_clr;
- res = _find_mono_in_reg(p_subkey + "\\" + default_clr, r_info, true);
- }
-
-cleanup:
- RegCloseKey(hKey);
- return res;
-}
-
-MonoRegInfo find_mono() {
- MonoRegInfo info;
-
- if (_find_mono_in_reg("Software\\Mono", info) == ERROR_SUCCESS) {
- return info;
- }
-
- if (_find_mono_in_reg_old("Software\\Novell\\Mono", info) == ERROR_SUCCESS) {
- return info;
- }
-
- return MonoRegInfo();
-}
-
-String find_msbuild_tools_path() {
- String msbuild_tools_path;
-
- // Try to find 15.0 with vswhere
-
- String vswhere_path = OS::get_singleton()->get_environment(sizeof(size_t) == 8 ? "ProgramFiles(x86)" : "ProgramFiles");
- vswhere_path += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
-
- List<String> vswhere_args;
- vswhere_args.push_back("-latest");
- vswhere_args.push_back("-products");
- vswhere_args.push_back("*");
- vswhere_args.push_back("-requires");
- vswhere_args.push_back("Microsoft.Component.MSBuild");
-
- String output;
- int exit_code;
- OS::get_singleton()->execute(vswhere_path, vswhere_args, &output, &exit_code);
-
- if (exit_code == 0) {
- Vector<String> lines = output.split("\n");
-
- for (int i = 0; i < lines.size(); i++) {
- const String &line = lines[i];
- int sep_idx = line.find(":");
-
- if (sep_idx > 0) {
- String key = line.substr(0, sep_idx); // No need to trim
-
- if (key == "installationPath") {
- String val = line.substr(sep_idx + 1, line.length()).strip_edges();
-
- ERR_BREAK(val.is_empty());
-
- if (!val.ends_with("\\")) {
- val += "\\";
- }
-
- // Since VS2019, the directory is simply named "Current"
- String msbuild_dir = val + "MSBuild\\Current\\Bin";
- if (DirAccess::exists(msbuild_dir)) {
- return msbuild_dir;
- }
-
- // Directory name "15.0" is used in VS 2017
- return val + "MSBuild\\15.0\\Bin";
- }
- }
- }
- }
-
- // Try to find 14.0 in the Registry
-
- HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\14.0", &hKey);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "MSBuildToolsPath", msbuild_tools_path);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
-cleanup:
- RegCloseKey(hKey);
-
- return msbuild_tools_path;
-}
-} // namespace MonoRegUtils
-
-#endif // WINDOWS_ENABLED
diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h
deleted file mode 100644
index 5be60d4930..0000000000
--- a/modules/mono/utils/mono_reg_utils.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*************************************************************************/
-/* mono_reg_utils.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 MONO_REG_UTILS_H
-#define MONO_REG_UTILS_H
-
-#ifdef WINDOWS_ENABLED
-
-#include "core/string/ustring.h"
-
-struct MonoRegInfo {
- String version;
- String install_root_dir;
- String assembly_dir;
- String config_dir;
- String bin_dir;
-};
-
-namespace MonoRegUtils {
-
-MonoRegInfo find_mono();
-String find_msbuild_tools_path();
-} // namespace MonoRegUtils
-
-#endif // WINDOWS_ENABLED
-
-#endif // MONO_REG_UTILS_H
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index a1905dfcfe..19ad59a1bc 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -51,6 +51,37 @@
namespace path {
+String find_executable(const String &p_name) {
+#ifdef WINDOWS_ENABLED
+ Vector<String> exts = OS::get_singleton()->get_environment("PATHEXT").split(ENV_PATH_SEP, false);
+#endif
+ Vector<String> env_path = OS::get_singleton()->get_environment("PATH").split(ENV_PATH_SEP, false);
+
+ if (env_path.is_empty()) {
+ return String();
+ }
+
+ for (int i = 0; i < env_path.size(); i++) {
+ String p = path::join(env_path[i], p_name);
+
+#ifdef WINDOWS_ENABLED
+ for (int j = 0; j < exts.size(); j++) {
+ String p2 = p + exts[j].to_lower(); // lowercase to reduce risk of case mismatch warning
+
+ if (FileAccess::exists(p2)) {
+ return p2;
+ }
+ }
+#else
+ if (FileAccess::exists(p)) {
+ return p;
+ }
+#endif
+ }
+
+ return String();
+}
+
String cwd() {
#ifdef WINDOWS_ENABLED
const DWORD expected_size = ::GetCurrentDirectoryW(0, nullptr);
diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h
index 9a2c757361..d1c3d3ccfd 100644
--- a/modules/mono/utils/path_utils.h
+++ b/modules/mono/utils/path_utils.h
@@ -36,6 +36,8 @@
namespace path {
+String find_executable(const String &p_name);
+
String join(const String &p_a, const String &p_b);
String join(const String &p_a, const String &p_b, const String &p_c);
String join(const String &p_a, const String &p_b, const String &p_c, const String &p_d);
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 975f2d8332..b0f94310b8 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -65,7 +65,7 @@ int sfind(const String &p_text, int p_from) {
break;
case 1: {
char32_t c = src[read_pos];
- found = src[read_pos] == 's' || (c >= '0' && c <= '4');
+ found = src[read_pos] == 's' || (c >= '0' && c <= '5');
break;
}
default:
@@ -86,32 +86,13 @@ int sfind(const String &p_text, int p_from) {
}
} // namespace
-String sformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) {
+String sformat(const String &p_text, const String &p1, const String &p2,
+ const String &p3, const String &p4, const String &p5, const String &p6) {
if (p_text.length() < 2) {
return p_text;
}
- Array args;
-
- if (p1.get_type() != Variant::NIL) {
- args.push_back(p1);
-
- if (p2.get_type() != Variant::NIL) {
- args.push_back(p2);
-
- if (p3.get_type() != Variant::NIL) {
- args.push_back(p3);
-
- if (p4.get_type() != Variant::NIL) {
- args.push_back(p4);
-
- if (p5.get_type() != Variant::NIL) {
- args.push_back(p5);
- }
- }
- }
- }
- }
+ String args[6] = { p1, p2, p3, p4, p5, p6 };
String new_string;
@@ -125,7 +106,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const
int req_index = (c == 's' ? findex++ : c - '0');
new_string += p_text.substr(search_from, result - search_from);
- new_string += args[req_index].operator String();
+ new_string += args[req_index];
search_from = result + 2;
}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index fa4c5e89f4..b00dd9dde8 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -36,7 +36,8 @@
#include <stdarg.h>
-String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant());
+String sformat(const String &p_text, const String &p1 = String(), const String &p2 = String(),
+ const String &p3 = String(), const String &p4 = String(), const String &p5 = String(), const String &p6 = String());
#ifdef TOOLS_ENABLED
bool is_csharp_keyword(const String &p_name);