summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/SCsub42
-rw-r--r--modules/arkit/SCsub3
-rw-r--r--modules/arkit/arkit_interface.h34
-rw-r--r--modules/arkit/arkit_interface.mm371
-rw-r--r--modules/arkit/arkit_session_delegate.h6
-rw-r--r--modules/arkit/register_types.h5
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp14
-rw-r--r--modules/assimp/editor_scene_importer_assimp.h6
-rw-r--r--modules/assimp/import_state.h3
-rw-r--r--modules/basis_universal/texture_basisu.h5
-rw-r--r--modules/bmp/image_loader_bmp.cpp31
-rw-r--r--modules/bullet/SCsub4
-rw-r--r--modules/bullet/area_bullet.cpp28
-rw-r--r--modules/bullet/area_bullet.h26
-rw-r--r--modules/bullet/bullet_physics_server.cpp42
-rw-r--r--modules/bullet/bullet_physics_server.h380
-rw-r--r--modules/bullet/bullet_types_converter.cpp59
-rw-r--r--modules/bullet/collision_object_bullet.cpp76
-rw-r--r--modules/bullet/collision_object_bullet.h50
-rw-r--r--modules/bullet/rigid_body_bullet.cpp160
-rw-r--r--modules/bullet/rigid_body_bullet.h126
-rw-r--r--modules/bullet/shape_bullet.cpp60
-rw-r--r--modules/bullet/shape_bullet.h30
-rw-r--r--modules/bullet/soft_body_bullet.cpp24
-rw-r--r--modules/bullet/soft_body_bullet.h20
-rw-r--r--modules/bullet/space_bullet.cpp142
-rw-r--r--modules/bullet/space_bullet.h43
-rw-r--r--modules/camera/SCsub3
-rw-r--r--modules/camera/camera_ios.mm93
-rw-r--r--modules/camera/camera_osx.mm6
-rw-r--r--modules/camera/camera_win.cpp16
-rw-r--r--modules/camera/camera_win.h2
-rw-r--r--modules/camera/register_types.h5
-rw-r--r--modules/csg/csg.cpp57
-rw-r--r--modules/csg/csg_gizmos.cpp8
-rw-r--r--modules/csg/csg_gizmos.h20
-rw-r--r--modules/csg/csg_shape.cpp22
-rw-r--r--modules/csg/csg_shape.h22
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml4
-rw-r--r--modules/cvtt/register_types.h8
-rw-r--r--modules/dds/texture_loader_dds.cpp10
-rw-r--r--modules/denoise/SCsub1
-rw-r--r--modules/denoise/config.py8
-rw-r--r--modules/denoise/lightmap_denoiser.h2
-rw-r--r--modules/denoise/register_types.h5
-rw-r--r--modules/denoise/resource_to_cpp.py2
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml8
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp7
-rw-r--r--modules/enet/networked_multiplayer_enet.h28
-rw-r--r--modules/etc/image_etc.cpp2
-rw-r--r--modules/gdnative/SCsub3
-rw-r--r--modules/gdnative/config.py1
-rw-r--r--modules/gdnative/doc_classes/@NativeScript.xml13
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml4
-rw-r--r--modules/gdnative/gdnative.cpp20
-rw-r--r--modules/gdnative/gdnative/aabb.cpp9
-rw-r--r--modules/gdnative/gdnative/array.cpp34
-rw-r--r--modules/gdnative/gdnative/basis.cpp2
-rw-r--r--modules/gdnative/gdnative/callable.cpp252
-rw-r--r--modules/gdnative/gdnative/color.cpp2
-rw-r--r--modules/gdnative/gdnative/dictionary.cpp2
-rw-r--r--modules/gdnative/gdnative/gdnative.cpp7
-rw-r--r--modules/gdnative/gdnative/node_path.cpp2
-rw-r--r--modules/gdnative/gdnative/packed_arrays.cpp (renamed from modules/gdnative/gdnative/pool_arrays.cpp)510
-rw-r--r--modules/gdnative/gdnative/plane.cpp9
-rw-r--r--modules/gdnative/gdnative/quat.cpp2
-rw-r--r--modules/gdnative/gdnative/rect2.cpp155
-rw-r--r--modules/gdnative/gdnative/rid.cpp2
-rw-r--r--modules/gdnative/gdnative/string.cpp670
-rw-r--r--modules/gdnative/gdnative/string_name.cpp2
-rw-r--r--modules/gdnative/gdnative/transform.cpp2
-rw-r--r--modules/gdnative/gdnative/transform2d.cpp2
-rw-r--r--modules/gdnative/gdnative/variant.cpp133
-rw-r--r--modules/gdnative/gdnative/vector2.cpp151
-rw-r--r--modules/gdnative/gdnative/vector3.cpp146
-rw-r--r--modules/gdnative/gdnative_api.json6243
-rw-r--r--modules/gdnative/gdnative_builders.py15
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp21
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.h10
-rw-r--r--modules/gdnative/include/gdnative/aabb.h2
-rw-r--r--modules/gdnative/include/gdnative/array.h8
-rw-r--r--modules/gdnative/include/gdnative/callable.h126
-rw-r--r--modules/gdnative/include/gdnative/gdnative.h18
-rw-r--r--modules/gdnative/include/gdnative/packed_arrays.h (renamed from modules/gdnative/include/gdnative/pool_arrays.h)243
-rw-r--r--modules/gdnative/include/gdnative/plane.h2
-rw-r--r--modules/gdnative/include/gdnative/rect2.h54
-rw-r--r--modules/gdnative/include/gdnative/rid.h2
-rw-r--r--modules/gdnative/include/gdnative/string.h164
-rw-r--r--modules/gdnative/include/gdnative/variant.h40
-rw-r--r--modules/gdnative/include/gdnative/vector2.h55
-rw-r--r--modules/gdnative/include/gdnative/vector3.h53
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h81
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp9
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp20
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp34
-rw-r--r--modules/gdnative/nativescript/nativescript.h87
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.h28
-rw-r--r--modules/gdnative/net/packet_peer_gdnative.h8
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.h10
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h6
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h66
-rw-r--r--modules/gdnative/register_types.cpp108
-rw-r--r--modules/gdnative/tests/test_string.h1980
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp5
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.h36
-rw-r--r--modules/gdnative/xr/xr_interface_gdnative.h32
-rw-r--r--modules/gdnavigation/nav_map.cpp5
-rw-r--r--modules/gdnavigation/navigation_mesh_editor_plugin.cpp6
-rw-r--r--modules/gdnavigation/navigation_mesh_editor_plugin.h14
-rw-r--r--modules/gdnavigation/navigation_mesh_generator.cpp4
-rw-r--r--modules/gdscript/SCsub4
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml243
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml2
-rw-r--r--modules/gdscript/doc_classes/GDScriptFunctionState.xml5
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp382
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h29
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp351
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h (renamed from modules/mono/editor/csharp_project.cpp)65
-rw-r--r--modules/gdscript/gdscript.cpp558
-rw-r--r--modules/gdscript/gdscript.h132
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp3346
-rw-r--r--modules/gdscript/gdscript_analyzer.h122
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp736
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h277
-rw-r--r--modules/gdscript/gdscript_cache.cpp255
-rw-r--r--modules/gdscript/gdscript_cache.h (renamed from modules/mono/glue/gd_glue.h)125
-rw-r--r--modules/gdscript/gdscript_codegen.h160
-rw-r--r--modules/gdscript/gdscript_compiler.cpp3135
-rw-r--r--modules/gdscript/gdscript_compiler.h160
-rw-r--r--modules/gdscript/gdscript_editor.cpp3467
-rw-r--r--modules/gdscript/gdscript_function.cpp720
-rw-r--r--modules/gdscript/gdscript_function.h22
-rw-r--r--modules/gdscript/gdscript_functions.cpp8
-rw-r--r--modules/gdscript/gdscript_parser.cpp11149
-rw-r--r--modules/gdscript/gdscript_parser.h1629
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp2536
-rw-r--r--modules/gdscript/gdscript_tokenizer.h445
-rw-r--r--modules/gdscript/gdscript_warning.cpp210
-rw-r--r--modules/gdscript/gdscript_warning.h87
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp604
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp13
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h6
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp1
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp1
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp15
-rw-r--r--modules/gdscript/language_server/lsp.hpp6
-rw-r--r--modules/gdscript/register_types.cpp121
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp231
-rw-r--r--modules/gdscript/tests/test_gdscript.h (renamed from modules/mono/editor/csharp_project.h)21
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml42
-rw-r--r--modules/gridmap/grid_map.cpp80
-rw-r--r--modules/gridmap/grid_map.h19
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp64
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h19
-rw-r--r--modules/jsonrpc/jsonrpc.cpp6
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp12
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h32
-rw-r--r--modules/lightmapper_rd/lm_blendseams.glsl53
-rw-r--r--modules/lightmapper_rd/lm_common_inc.glsl7
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl22
-rw-r--r--modules/lightmapper_rd/lm_raster.glsl22
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp169
-rw-r--r--modules/mbedtls/crypto_mbedtls.h15
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp6
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h24
-rw-r--r--modules/mobile_vr/register_types.cpp8
-rw-r--r--modules/modules_builders.py11
-rw-r--r--modules/mono/SCsub2
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py4
-rw-r--r--modules/mono/build_scripts/mono_configure.py18
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py10
-rw-r--r--modules/mono/build_scripts/solution_builder.py7
-rw-r--r--modules/mono/class_db_api_json.cpp6
-rw-r--r--modules/mono/config.py12
-rw-r--r--modules/mono/csharp_script.cpp435
-rw-r--r--modules/mono/csharp_script.h259
-rw-r--r--modules/mono/doc_classes/@C#.xml13
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml5
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml17
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln16
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj35
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec22
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props112
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets17
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs30
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs48
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs270
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs56
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs118
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs165
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs329
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs80
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs43
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs17
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildManager.cs51
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildTab.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs116
-rwxr-xr-xmodules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs28
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs153
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs42
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs48
-rw-r--r--modules/mono/editor/bindings_generator.cpp495
-rw-r--r--modules/mono/editor/bindings_generator.h140
-rw-r--r--modules/mono/editor/code_completion.cpp31
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp2
-rw-r--r--modules/mono/editor/godotsharp_export.cpp47
-rw-r--r--modules/mono/editor/script_class_parser.cpp61
-rw-r--r--modules/mono/editor/script_class_parser.h1
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs234
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs34
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs294
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs629
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs329
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs49
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs148
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs232
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs183
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs161
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs50
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs144
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs180
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs327
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs159
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs371
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs143
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj36
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj48
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs25
-rw-r--r--modules/mono/glue/arguments_vector.h4
-rw-r--r--modules/mono/glue/base_object_glue.cpp12
-rw-r--r--modules/mono/glue/base_object_glue.h73
-rw-r--r--modules/mono/glue/collections_glue.cpp28
-rw-r--r--modules/mono/glue/collections_glue.h124
-rw-r--r--modules/mono/glue/gd_glue.cpp16
-rw-r--r--modules/mono/glue/glue_header.h18
-rw-r--r--modules/mono/glue/nodepath_glue.cpp9
-rw-r--r--modules/mono/glue/nodepath_glue.h68
-rw-r--r--modules/mono/glue/rid_glue.cpp9
-rw-r--r--modules/mono/glue/rid_glue.h53
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp (renamed from modules/mono/glue/string_name_glue.h)56
-rw-r--r--modules/mono/glue/string_glue.cpp4
-rw-r--r--modules/mono/glue/string_glue.h56
-rw-r--r--modules/mono/glue/string_name_glue.cpp7
-rw-r--r--modules/mono/godotsharp_dirs.cpp2
-rw-r--r--modules/mono/managed_callable.cpp6
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp152
-rw-r--r--modules/mono/mono_gd/gd_mono.h27
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp75
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp69
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp12
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp7
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp34
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp383
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h28
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp18
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp18
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp35
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h26
-rw-r--r--modules/mono/register_types.cpp6
-rw-r--r--modules/mono/register_types.h5
-rw-r--r--modules/mono/signal_awaiter_utils.cpp18
-rw-r--r--modules/mono/utils/macros.h2
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp9
-rw-r--r--modules/mono/utils/osx_utils.cpp23
-rw-r--r--modules/mono/utils/path_utils.cpp72
-rw-r--r--modules/mono/utils/path_utils.h2
-rw-r--r--modules/mono/utils/string_utils.cpp18
-rw-r--r--modules/opensimplex/noise_texture.h12
-rw-r--r--modules/opensimplex/open_simplex_noise.cpp2
-rw-r--r--modules/opus/SCsub3
-rw-r--r--modules/opus/config.py2
-rw-r--r--modules/pvr/texture_loader_pvr.cpp8
-rw-r--r--modules/regex/SCsub2
-rw-r--r--modules/regex/doc_classes/RegEx.xml16
-rw-r--r--modules/regex/doc_classes/RegExMatch.xml2
-rw-r--r--modules/regex/regex.cpp229
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp8
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.h22
-rw-r--r--modules/stb_vorbis/resource_importer_ogg_vorbis.h20
-rw-r--r--modules/tga/image_loader_tga.cpp14
-rw-r--r--modules/theora/config.py2
-rw-r--r--modules/theora/doc_classes/VideoStreamTheora.xml5
-rw-r--r--modules/theora/video_stream_theora.cpp11
-rw-r--r--modules/theora/video_stream_theora.h36
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp158
-rw-r--r--modules/visual_script/config.py1
-rw-r--r--modules/visual_script/doc_classes/@VisualScript.xml15
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml2
-rw-r--r--modules/visual_script/visual_script.cpp8
-rw-r--r--modules/visual_script/visual_script.h64
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp2
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h20
-rw-r--r--modules/visual_script/visual_script_editor.cpp48
-rw-r--r--modules/visual_script/visual_script_editor.h68
-rw-r--r--modules/visual_script/visual_script_expression.cpp16
-rw-r--r--modules/visual_script/visual_script_expression.h22
-rw-r--r--modules/visual_script/visual_script_flow_control.h158
-rw-r--r--modules/visual_script/visual_script_func_nodes.h98
-rw-r--r--modules/visual_script/visual_script_nodes.cpp52
-rw-r--r--modules/visual_script/visual_script_nodes.h602
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp10
-rw-r--r--modules/visual_script/visual_script_yield_nodes.h48
-rw-r--r--modules/vorbis/config.py2
-rw-r--r--modules/webm/config.py5
-rw-r--r--modules/webm/doc_classes/VideoStreamWebm.xml3
-rw-r--r--modules/webm/libvpx/SCsub4
-rw-r--r--modules/webm/video_stream_webm.h36
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml4
-rw-r--r--modules/webrtc/webrtc_data_channel.h7
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.h42
-rw-r--r--modules/webrtc/webrtc_data_channel_js.h42
-rw-r--r--modules/webrtc/webrtc_multiplayer.h30
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.h26
-rw-r--r--modules/websocket/editor_debugger_server_websocket.h12
-rw-r--r--modules/websocket/register_types.cpp9
-rw-r--r--modules/websocket/websocket_client.cpp2
-rw-r--r--modules/websocket/websocket_client.h6
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h23
-rw-r--r--modules/websocket/websocket_peer.h5
-rw-r--r--modules/websocket/websocket_server.h8
-rw-r--r--modules/xatlas_unwrap/register_types.cpp2
343 files changed, 33510 insertions, 23295 deletions
diff --git a/modules/SCsub b/modules/SCsub
index fb46c5f877..edfc4ed9c6 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -1,28 +1,56 @@
#!/usr/bin/env python
-Import("env")
-
import modules_builders
+import os
+
+Import("env")
env_modules = env.Clone()
Export("env_modules")
# Header with MODULE_*_ENABLED defines.
-env.CommandNoCache("modules_enabled.gen.h", Value(env.module_list), modules_builders.generate_modules_enabled)
+env.CommandNoCache(
+ "modules_enabled.gen.h",
+ Value(env.module_list),
+ env.Run(
+ modules_builders.generate_modules_enabled,
+ "Generating enabled modules header.",
+ # NOTE: No need to run in subprocess since this is still executed serially.
+ subprocess=False,
+ ),
+)
+
+# Header to be included in `tests/test_main.cpp` to run module-specific tests.
+if env["tests"]:
+ env.CommandNoCache(
+ "modules_tests.gen.h",
+ Value(env.module_list),
+ env.Run(
+ modules_builders.generate_modules_tests,
+ "Generating modules tests header.",
+ # NOTE: No need to run in subprocess since this is still executed serially.
+ subprocess=False,
+ ),
+ )
+ env.AlwaysBuild("modules_tests.gen.h")
vs_sources = []
# libmodule_<name>.a for each active module.
-for module in env.module_list:
+for name, path in env.module_list.items():
env.modules_sources = []
- SConscript(module + "/SCsub")
+
+ if not os.path.isabs(path):
+ SConscript(name + "/SCsub") # Built-in.
+ else:
+ SConscript(path + "/SCsub") # Custom.
# Some modules are not linked automatically but can be enabled optionally
# on iOS, so we handle those specially.
- if env["platform"] == "iphone" and module in ["arkit", "camera"]:
+ if env["platform"] == "iphone" and name in ["arkit", "camera"]:
continue
- lib = env_modules.add_library("module_%s" % module, env.modules_sources)
+ lib = env_modules.add_library("module_%s" % name, env.modules_sources)
env.Prepend(LIBS=[lib])
if env["vsproj"]:
vs_sources += env.modules_sources
diff --git a/modules/arkit/SCsub b/modules/arkit/SCsub
index 61c0a8248c..7e103d6565 100644
--- a/modules/arkit/SCsub
+++ b/modules/arkit/SCsub
@@ -5,6 +5,9 @@ Import("env_modules")
env_arkit = env_modules.Clone()
+# (iOS) Enable module support
+env_arkit.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
# (iOS) Build as separate static library
modules_sources = []
env_arkit.add_source_files(modules_sources, "*.cpp")
diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h
index 1044f3cf6f..5a2c50e213 100644
--- a/modules/arkit/arkit_interface.h
+++ b/modules/arkit/arkit_interface.h
@@ -60,8 +60,8 @@ private:
float eye_height, z_near, z_far;
Ref<CameraFeed> feed;
- int image_width[2];
- int image_height[2];
+ size_t image_width[2];
+ size_t image_height[2];
Vector<uint8_t> img_data[2];
struct anchor_map {
@@ -84,9 +84,9 @@ public:
void start_session();
void stop_session();
- bool get_anchor_detection_is_enabled() const;
- void set_anchor_detection_is_enabled(bool p_enable);
- virtual int get_camera_feed_id();
+ bool get_anchor_detection_is_enabled() const override;
+ void set_anchor_detection_is_enabled(bool p_enable) override;
+ virtual int get_camera_feed_id() override;
bool get_light_estimation_is_enabled() const;
void set_light_estimation_is_enabled(bool p_enable);
@@ -97,22 +97,22 @@ public:
/* while Godot has its own raycast logic this takes ARKits camera into account and hits on any ARAnchor */
Array raycast(Vector2 p_screen_coord);
- void notification(int p_what);
+ virtual void notification(int p_what) override;
- virtual StringName get_name() const;
- virtual int get_capabilities() const;
+ virtual StringName get_name() const override;
+ virtual int get_capabilities() const override;
- virtual bool is_initialized() const;
- virtual bool initialize();
- virtual void uninitialize();
+ virtual bool is_initialized() const override;
+ virtual bool initialize() override;
+ virtual void uninitialize() override;
- virtual Size2 get_render_targetsize();
- virtual bool is_stereo();
- virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform);
- virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far);
- virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect);
+ virtual Size2 get_render_targetsize() override;
+ virtual bool is_stereo() override;
+ virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override;
+ virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
+ virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
- virtual void process();
+ virtual void process() override;
// called by delegate (void * because C++ and Obj-C don't always mix, should really change all platform/iphone/*.cpp files to .mm)
void _add_or_update_anchor(void *p_anchor);
diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm
index 7de824815a..3fb2cc933d 100644
--- a/modules/arkit/arkit_interface.mm
+++ b/modules/arkit/arkit_interface.mm
@@ -42,7 +42,9 @@
#include "arkit_session_delegate.h"
// just a dirty workaround for now, declare these as globals. I'll probably encapsulate ARSession and associated logic into an mm object and change ARKitInterface to a normal cpp object that consumes it.
+API_AVAILABLE(ios(11.0))
ARSession *ar_session;
+
ARKitSessionDelegate *ar_delegate;
NSTimeInterval last_timestamp;
@@ -55,22 +57,28 @@ void ARKitInterface::start_session() {
if (initialized) {
print_line("Starting ARKit session");
- Class ARWorldTrackingConfigurationClass = NSClassFromString(@"ARWorldTrackingConfiguration");
- ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfigurationClass new];
+ if (@available(iOS 11, *)) {
+ Class ARWorldTrackingConfigurationClass = NSClassFromString(@"ARWorldTrackingConfiguration");
+ ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfigurationClass new];
- configuration.lightEstimationEnabled = light_estimation_is_enabled;
- if (plane_detection_is_enabled) {
- configuration.planeDetection = ARPlaneDetectionVertical | ARPlaneDetectionHorizontal;
- } else {
- configuration.planeDetection = 0;
- }
+ configuration.lightEstimationEnabled = light_estimation_is_enabled;
+ if (plane_detection_is_enabled) {
+ if (@available(iOS 11.3, *)) {
+ configuration.planeDetection = ARPlaneDetectionVertical | ARPlaneDetectionHorizontal;
+ } else {
+ configuration.planeDetection = ARPlaneDetectionHorizontal;
+ }
+ } else {
+ configuration.planeDetection = 0;
+ }
- // make sure our camera is on
- if (feed.is_valid()) {
- feed->set_active(true);
- }
+ // make sure our camera is on
+ if (feed.is_valid()) {
+ feed->set_active(true);
+ }
- [ar_session runWithConfiguration:configuration];
+ [ar_session runWithConfiguration:configuration];
+ }
}
}
@@ -84,7 +92,9 @@ void ARKitInterface::stop_session() {
feed->set_active(false);
}
- [ar_session pause];
+ if (@available(iOS 11.0, *)) {
+ [ar_session pause];
+ }
}
}
@@ -92,12 +102,12 @@ void ARKitInterface::notification(int p_what) {
// TODO, this is not being called, need to find out why, possibly because this is not a node.
// in that case we need to find a way to get these notifications!
switch (p_what) {
- case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
+ case DisplayServer::WINDOW_EVENT_FOCUS_IN: {
print_line("Focus in");
start_session();
}; break;
- case MainLoop::NOTIFICATION_WM_FOCUS_OUT: {
+ case DisplayServer::WINDOW_EVENT_FOCUS_OUT: {
print_line("Focus out");
stop_session();
@@ -162,37 +172,42 @@ int ARKitInterface::get_capabilities() const {
}
Array ARKitInterface::raycast(Vector2 p_screen_coord) {
- Array arr;
- Size2 screen_size = OS::get_singleton()->get_window_size();
- CGPoint point;
- point.x = p_screen_coord.x / screen_size.x;
- point.y = p_screen_coord.y / screen_size.y;
-
- ///@TODO maybe give more options here, for now we're taking just ARAchors into account that were found during plane detection keeping their size into account
- NSArray<ARHitTestResult *> *results = [ar_session.currentFrame hittest:point types:ARHitTestResultTypeExistingPlaneUsingExtent];
-
- for (ARHitTestResult *result in results) {
- Transform transform;
-
- matrix_float4x4 m44 = result.worldTransform;
- transform.basis.elements[0].x = m44.columns[0][0];
- transform.basis.elements[1].x = m44.columns[0][1];
- transform.basis.elements[2].x = m44.columns[0][2];
- transform.basis.elements[0].y = m44.columns[1][0];
- transform.basis.elements[1].y = m44.columns[1][1];
- transform.basis.elements[2].y = m44.columns[1][2];
- transform.basis.elements[0].z = m44.columns[2][0];
- transform.basis.elements[1].z = m44.columns[2][1];
- transform.basis.elements[2].z = m44.columns[2][2];
- transform.origin.x = m44.columns[3][0];
- transform.origin.y = m44.columns[3][1];
- transform.origin.z = m44.columns[3][2];
-
- /* important, NOT scaled to world_scale !! */
- arr.push_back(transform);
- }
+ if (@available(iOS 11, *)) {
+ Array arr;
+ Size2 screen_size = DisplayServer::get_singleton()->screen_get_size();
+ CGPoint point;
+ point.x = p_screen_coord.x / screen_size.x;
+ point.y = p_screen_coord.y / screen_size.y;
+
+ ///@TODO maybe give more options here, for now we're taking just ARAchors into account that were found during plane detection keeping their size into account
+
+ NSArray<ARHitTestResult *> *results = [ar_session.currentFrame hitTest:point types:ARHitTestResultTypeExistingPlaneUsingExtent];
+
+ for (ARHitTestResult *result in results) {
+ Transform transform;
+
+ matrix_float4x4 m44 = result.worldTransform;
+ transform.basis.elements[0].x = m44.columns[0][0];
+ transform.basis.elements[1].x = m44.columns[0][1];
+ transform.basis.elements[2].x = m44.columns[0][2];
+ transform.basis.elements[0].y = m44.columns[1][0];
+ transform.basis.elements[1].y = m44.columns[1][1];
+ transform.basis.elements[2].y = m44.columns[1][2];
+ transform.basis.elements[0].z = m44.columns[2][0];
+ transform.basis.elements[1].z = m44.columns[2][1];
+ transform.basis.elements[2].z = m44.columns[2][2];
+ transform.origin.x = m44.columns[3][0];
+ transform.origin.y = m44.columns[3][1];
+ transform.origin.z = m44.columns[3][2];
+
+ /* important, NOT scaled to world_scale !! */
+ arr.push_back(transform);
+ }
- return arr;
+ return arr;
+ } else {
+ return Array();
+ }
}
void ARKitInterface::_bind_methods() {
@@ -221,51 +236,55 @@ bool ARKitInterface::initialize() {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, false);
- if (!initialized) {
- print_line("initializing ARKit");
+ if (@available(iOS 11, *)) {
+ if (!initialized) {
+ print_line("initializing ARKit");
- // create our ar session and delegate
- Class ARSessionClass = NSClassFromString(@"ARSession");
- if (ARSessionClass == Nil) {
- void *arkit_handle = dlopen("/System/Library/Frameworks/ARKit.framework/ARKit", RTLD_NOW);
- if (arkit_handle) {
- ARSessionClass = NSClassFromString(@"ARSession");
- } else {
- print_line("ARKit init failed");
- return false;
+ // create our ar session and delegate
+ Class ARSessionClass = NSClassFromString(@"ARSession");
+ if (ARSessionClass == Nil) {
+ void *arkit_handle = dlopen("/System/Library/Frameworks/ARKit.framework/ARKit", RTLD_NOW);
+ if (arkit_handle) {
+ ARSessionClass = NSClassFromString(@"ARSession");
+ } else {
+ print_line("ARKit init failed");
+ return false;
+ }
}
- }
- ar_session = [ARSessionClass new];
- ar_delegate = [ARKitSessionDelegate new];
- ar_delegate.arkit_interface = this;
- ar_session.delegate = ar_delegate;
+ ar_session = [ARSessionClass new];
+ ar_delegate = [ARKitSessionDelegate new];
+ ar_delegate.arkit_interface = this;
+ ar_session.delegate = ar_delegate;
- // reset our transform
- transform = Transform();
+ // reset our transform
+ transform = Transform();
- // make this our primary interface
- xr_server->set_primary_interface(this);
+ // make this our primary interface
+ xr_server->set_primary_interface(this);
- // make sure we have our feed setup
- if (feed.is_null()) {
- feed.instance();
- feed->set_name("ARKit");
+ // make sure we have our feed setup
+ if (feed.is_null()) {
+ feed.instance();
+ feed->set_name("ARKit");
- CameraServer *cs = CameraServer::get_singleton();
- if (cs != NULL) {
- cs->add_feed(feed);
+ CameraServer *cs = CameraServer::get_singleton();
+ if (cs != NULL) {
+ cs->add_feed(feed);
+ }
}
- }
- feed->set_active(true);
+ feed->set_active(true);
- // yeah!
- initialized = true;
+ // yeah!
+ initialized = true;
- // Start our session...
- start_session();
- }
+ // Start our session...
+ start_session();
+ }
- return true;
+ return true;
+ } else {
+ return false;
+ }
}
void ARKitInterface::uninitialize() {
@@ -286,9 +305,12 @@ void ARKitInterface::uninitialize() {
remove_all_anchors();
- [ar_session release];
+ if (@available(iOS 11.0, *)) {
+ [ar_session release];
+ ar_session = NULL;
+ }
[ar_delegate release];
- ar_session = NULL;
+
ar_delegate = NULL;
initialized = false;
session_was_started = false;
@@ -296,15 +318,15 @@ void ARKitInterface::uninitialize() {
}
Size2 ARKitInterface::get_render_targetsize() {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
- Size2 target_size = OS::get_singleton()->get_window_size();
+ Size2 target_size = DisplayServer::get_singleton()->screen_get_size();
return target_size;
}
Transform ARKitInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
Transform transform_for_eye;
@@ -336,7 +358,7 @@ CameraMatrix ARKitInterface::get_projection_for_eye(XRInterface::Eyes p_eye, rea
}
void ARKitInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
// We must have a valid render target
ERR_FAIL_COND(!p_render_target.is_valid());
@@ -345,15 +367,15 @@ void ARKitInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target
ERR_FAIL_COND(p_screen_rect == Rect2());
// get the size of our screen
- Rect2 screen_rect = p_screen_rect;
+ // Rect2 screen_rect = p_screen_rect;
// screen_rect.position.x += screen_rect.size.x;
// screen_rect.size.x = -screen_rect.size.x;
// screen_rect.position.y += screen_rect.size.y;
// screen_rect.size.y = -screen_rect.size.y;
- VSG::rasterizer->set_current_render_target(RID());
- VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0);
+ // VSG::rasterizer->set_current_render_target(RID());
+ // VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0);
}
XRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char *p_uuid) {
@@ -432,7 +454,7 @@ void ARKitInterface::remove_all_anchors() {
}
void ARKitInterface::process() {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
if (@available(iOS 11.0, *)) {
if (initialized) {
@@ -443,8 +465,16 @@ void ARKitInterface::process() {
last_timestamp = current_frame.timestamp;
// get some info about our screen and orientation
- Size2 screen_size = OS::get_singleton()->get_window_size();
- UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
+ Size2 screen_size = DisplayServer::get_singleton()->screen_get_size();
+ UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
+
+ if (@available(iOS 13, *)) {
+ orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ orientation = [[UIApplication sharedApplication] statusBarOrientation];
+#endif
+ }
// Grab our camera image for our backbuffer
CVPixelBufferRef pixelBuffer = current_frame.capturedImage;
@@ -475,27 +505,27 @@ void ARKitInterface::process() {
{
// do Y
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
- int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
+ size_t bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
if ((image_width[0] != new_width) || (image_height[0] != new_height)) {
printf("- Camera padding l:%lu r:%lu t:%lu b:%lu\n", extraLeft, extraRight, extraTop, extraBottom);
- printf("- Camera Y plane size: %i, %i - %i\n", new_width, new_height, bytes_per_row);
+ printf("- Camera Y plane size: %lu, %lu - %lu\n", new_width, new_height, bytes_per_row);
image_width[0] = new_width;
image_height[0] = new_height;
img_data[0].resize(new_width * new_height);
}
- uint8_t *w = img_data[0].write();
+ uint8_t *w = img_data[0].ptrw();
if (new_width == bytes_per_row) {
- memcpy(w.ptr(), dataY, new_width * new_height);
+ memcpy(w, dataY, new_width * new_height);
} else {
- int offset_a = 0;
- int offset_b = extraLeft + (extraTop * bytes_per_row);
- for (int r = 0; r < new_height; r++) {
- memcpy(w.ptr() + offset_a, dataY + offset_b, new_width);
+ size_t offset_a = 0;
+ size_t offset_b = extraLeft + (extraTop * bytes_per_row);
+ for (size_t r = 0; r < new_height; r++) {
+ memcpy(w + offset_a, dataY + offset_b, new_width);
offset_a += new_width;
offset_b += bytes_per_row;
}
@@ -507,26 +537,26 @@ void ARKitInterface::process() {
{
// do CbCr
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
- int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
+ size_t bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
if ((image_width[1] != new_width) || (image_height[1] != new_height)) {
- printf("- Camera CbCr plane size: %i, %i - %i\n", new_width, new_height, bytes_per_row);
+ printf("- Camera CbCr plane size: %lu, %lu - %lu\n", new_width, new_height, bytes_per_row);
image_width[1] = new_width;
image_height[1] = new_height;
img_data[1].resize(2 * new_width * new_height);
}
- uint8_t *w = img_data[1].write();
+ uint8_t *w = img_data[1].ptrw();
if ((2 * new_width) == bytes_per_row) {
- memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height);
+ memcpy(w, dataCbCr, 2 * new_width * new_height);
} else {
- int offset_a = 0;
- int offset_b = extraLeft + (extraTop * bytes_per_row);
- for (int r = 0; r < new_height; r++) {
- memcpy(w.ptr() + offset_a, dataCbCr + offset_b, 2 * new_width);
+ size_t offset_a = 0;
+ size_t offset_b = extraLeft + (extraTop * bytes_per_row);
+ for (size_t r = 0; r < new_height; r++) {
+ memcpy(w + offset_a, dataCbCr + offset_b, 2 * new_width);
offset_a += 2 * new_width;
offset_b += bytes_per_row;
}
@@ -658,69 +688,78 @@ void ARKitInterface::process() {
}
void ARKitInterface::_add_or_update_anchor(void *p_anchor) {
- _THREAD_SAFE_METHOD_
-
- ARAnchor *anchor = (ARAnchor *)p_anchor;
-
- unsigned char uuid[16];
- [anchor.identifier getUUIDBytes:uuid];
-
- XRPositionalTracker *tracker = get_anchor_for_uuid(uuid);
- if (tracker != NULL) {
- // lets update our mesh! (using Arjens code as is for now)
- // we should also probably limit how often we do this...
+ // _THREAD_SAFE_METHOD_
- // can we safely cast this?
- ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
-
- if (planeAnchor.geometry.triangleCount > 0) {
- Ref<SurfaceTool> surftool;
- surftool.instance();
- surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ if (@available(iOS 11.0, *)) {
+ ARAnchor *anchor = (ARAnchor *)p_anchor;
+
+ unsigned char uuid[16];
+ [anchor.identifier getUUIDBytes:uuid];
+
+ XRPositionalTracker *tracker = get_anchor_for_uuid(uuid);
+ if (tracker != NULL) {
+ // lets update our mesh! (using Arjens code as is for now)
+ // we should also probably limit how often we do this...
+
+ // can we safely cast this?
+ ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
+
+ if (@available(iOS 11.3, *)) {
+ if (planeAnchor.geometry.triangleCount > 0) {
+ Ref<SurfaceTool> surftool;
+ surftool.instance();
+ surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ for (int j = planeAnchor.geometry.triangleCount * 3 - 1; j >= 0; j--) {
+ int16_t index = planeAnchor.geometry.triangleIndices[j];
+ simd_float3 vrtx = planeAnchor.geometry.vertices[index];
+ simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index];
+ surftool->add_uv(Vector2(textcoord[0], textcoord[1]));
+ surftool->add_color(Color(0.8, 0.8, 0.8));
+ surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2]));
+ }
- for (int j = planeAnchor.geometry.triangleCount * 3 - 1; j >= 0; j--) {
- int16_t index = planeAnchor.geometry.triangleIndices[j];
- simd_float3 vrtx = planeAnchor.geometry.vertices[index];
- simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index];
- surftool->add_uv(Vector2(textcoord[0], textcoord[1]));
- surftool->add_color(Color(0.8, 0.8, 0.8));
- surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2]));
+ surftool->generate_normals();
+ tracker->set_mesh(surftool->commit());
+ } else {
+ Ref<Mesh> nomesh;
+ tracker->set_mesh(nomesh);
+ }
+ } else {
+ Ref<Mesh> nomesh;
+ tracker->set_mesh(nomesh);
}
- surftool->generate_normals();
- tracker->set_mesh(surftool->commit());
- } else {
- Ref<Mesh> nomesh;
- tracker->set_mesh(nomesh);
+ // Note, this also contains a scale factor which gives us an idea of the size of the anchor
+ // We may extract that in our XRAnchor class
+ Basis b;
+ matrix_float4x4 m44 = anchor.transform;
+ b.elements[0].x = m44.columns[0][0];
+ b.elements[1].x = m44.columns[0][1];
+ b.elements[2].x = m44.columns[0][2];
+ b.elements[0].y = m44.columns[1][0];
+ b.elements[1].y = m44.columns[1][1];
+ b.elements[2].y = m44.columns[1][2];
+ b.elements[0].z = m44.columns[2][0];
+ b.elements[1].z = m44.columns[2][1];
+ b.elements[2].z = m44.columns[2][2];
+ tracker->set_orientation(b);
+ tracker->set_rw_position(Vector3(m44.columns[3][0], m44.columns[3][1], m44.columns[3][2]));
}
-
- // Note, this also contains a scale factor which gives us an idea of the size of the anchor
- // We may extract that in our XRAnchor class
- Basis b;
- matrix_float4x4 m44 = anchor.transform;
- b.elements[0].x = m44.columns[0][0];
- b.elements[1].x = m44.columns[0][1];
- b.elements[2].x = m44.columns[0][2];
- b.elements[0].y = m44.columns[1][0];
- b.elements[1].y = m44.columns[1][1];
- b.elements[2].y = m44.columns[1][2];
- b.elements[0].z = m44.columns[2][0];
- b.elements[1].z = m44.columns[2][1];
- b.elements[2].z = m44.columns[2][2];
- tracker->set_orientation(b);
- tracker->set_rw_position(Vector3(m44.columns[3][0], m44.columns[3][1], m44.columns[3][2]));
}
}
void ARKitInterface::_remove_anchor(void *p_anchor) {
- _THREAD_SAFE_METHOD_
+ // _THREAD_SAFE_METHOD_
- ARAnchor *anchor = (ARAnchor *)p_anchor;
+ if (@available(iOS 11.0, *)) {
+ ARAnchor *anchor = (ARAnchor *)p_anchor;
- unsigned char uuid[16];
- [anchor.identifier getUUIDBytes:uuid];
+ unsigned char uuid[16];
+ [anchor.identifier getUUIDBytes:uuid];
- remove_anchor_for_uuid(uuid);
+ remove_anchor_for_uuid(uuid);
+ }
}
ARKitInterface::ARKitInterface() {
@@ -728,7 +767,9 @@ ARKitInterface::ARKitInterface() {
session_was_started = false;
plane_detection_is_enabled = false;
light_estimation_is_enabled = false;
- ar_session = NULL;
+ if (@available(iOS 11.0, *)) {
+ ar_session = NULL;
+ }
z_near = 0.01;
z_far = 1000.0;
projection.set_perspective(60.0, 1.0, z_near, z_far, false);
diff --git a/modules/arkit/arkit_session_delegate.h b/modules/arkit/arkit_session_delegate.h
index 158b80a60a..df98bf506e 100644
--- a/modules/arkit/arkit_session_delegate.h
+++ b/modules/arkit/arkit_session_delegate.h
@@ -42,9 +42,9 @@ class ARKitInterface;
@property(nonatomic) ARKitInterface *arkit_interface;
-- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors;
-- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors;
-- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors;
+- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
+- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
+- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
@end
#endif /* !ARKIT_SESSION_DELEGATE_H */
diff --git a/modules/arkit/register_types.h b/modules/arkit/register_types.h
index 5c697baf68..f8939a1e3f 100644
--- a/modules/arkit/register_types.h
+++ b/modules/arkit/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef ARKIT_REGISTER_TYPES_H
+#define ARKIT_REGISTER_TYPES_H
+
void register_arkit_types();
void unregister_arkit_types();
+
+#endif // ARKIT_REGISTER_TYPES_H
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
index 9c90faf66b..e5becfd559 100644
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ b/modules/assimp/editor_scene_importer_assimp.cpp
@@ -44,7 +44,6 @@
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
#include <assimp/LogStream.hpp>
-#include <string>
// move into assimp
aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) {
@@ -102,8 +101,6 @@ void EditorSceneImporterAssimp::_bind_methods() {
Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
Assimp::Importer importer;
- std::wstring w_path = ProjectSettings::get_singleton()->globalize_path(p_path).c_str();
- std::string s_path(w_path.begin(), w_path.end());
importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true);
// Cannot remove pivot points because the static mesh will be in the wrong place
importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
@@ -145,7 +142,8 @@ Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_f
// aiProcess_EmbedTextures |
//aiProcess_SplitByBoneCount |
0;
- aiScene *scene = (aiScene *)importer.ReadFile(s_path.c_str(), post_process_Steps);
+ String g_path = ProjectSettings::get_singleton()->globalize_path(p_path);
+ aiScene *scene = (aiScene *)importer.ReadFile(g_path.utf8().ptr(), post_process_Steps);
ERR_FAIL_COND_V_MSG(scene == nullptr, nullptr, String("Open Asset Import failed to open: ") + String(importer.GetErrorString()));
@@ -298,6 +296,7 @@ EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene,
state.assimp_scene = scene;
state.max_bone_weights = p_max_bone_weights;
state.animation_player = nullptr;
+ state.import_flags = p_flags;
// populate light map
for (unsigned int l = 0; l < scene->mNumLights; l++) {
@@ -442,7 +441,6 @@ EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene,
Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation);
skeleton->add_bone(bone_name);
skeleton->set_bone_rest(boneIdx, pform);
- skeleton->set_bone_pose(boneIdx, pform);
if (parent_node != nullptr) {
int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName));
@@ -613,7 +611,7 @@ void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, cons
xform.basis.set_quat_scale(rot, scale);
xform.origin = pos;
- xform = skeleton->get_bone_pose(skeleton_bone).inverse() * xform;
+ xform = skeleton->get_bone_rest(skeleton_bone).inverse() * xform;
rot = xform.basis.get_rotation_quat();
rot.normalize();
@@ -829,6 +827,8 @@ EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &stat
Ref<ArrayMesh> mesh;
mesh.instance();
bool has_uvs = false;
+ bool compress_vert_data = state.import_flags & IMPORT_USE_COMPRESSION;
+ uint32_t mesh_flags = compress_vert_data ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
Map<String, uint32_t> morph_mesh_string_lookup;
@@ -1266,7 +1266,7 @@ EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &stat
morphs[j] = array_copy;
}
- mesh->add_surface_from_arrays(primitive, array_mesh, morphs);
+ mesh->add_surface_from_arrays(primitive, array_mesh, morphs, Dictionary(), mesh_flags);
mesh->surface_set_material(i, mat);
mesh->surface_set_name(i, AssimpUtils::get_assimp_string(ai_mesh->mName));
}
diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h
index ab00a6c3aa..7be80c4ad0 100644
--- a/modules/assimp/editor_scene_importer_assimp.h
+++ b/modules/assimp/editor_scene_importer_assimp.h
@@ -136,9 +136,9 @@ public:
Assimp::DefaultLogger::kill();
}
- virtual void get_extensions(List<String> *r_extensions) const;
- virtual uint32_t get_import_flags() const;
- virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr);
+ virtual void get_extensions(List<String> *r_extensions) const override;
+ virtual uint32_t get_import_flags() const override;
+ virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override;
Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path);
static void RegenerateBoneStack(ImportState &state);
diff --git a/modules/assimp/import_state.h b/modules/assimp/import_state.h
index 4a3bd17acb..ee22800ac4 100644
--- a/modules/assimp/import_state.h
+++ b/modules/assimp/import_state.h
@@ -87,6 +87,9 @@ struct ImportState {
// this means we can detect
// what bones are for other armatures
List<aiBone *> bone_stack;
+
+ // EditorSceneImporter::ImportFlags
+ uint32_t import_flags;
};
struct AssimpImageData {
diff --git a/modules/basis_universal/texture_basisu.h b/modules/basis_universal/texture_basisu.h
index 8de151ede0..20ecf15a59 100644
--- a/modules/basis_universal/texture_basisu.h
+++ b/modules/basis_universal/texture_basisu.h
@@ -28,6 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef BASIS_UNIVERSAL_TEXTURE_BASISU_H
+#define BASIS_UNIVERSAL_TEXTURE_BASISU_H
+
#include "scene/resources/texture.h"
#ifdef TOOLS_ENABLED
@@ -75,3 +78,5 @@ public:
};
#endif
+
+#endif // BASIS_UNIVERSAL_TEXTURE_BASISU_H
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index ac4e534983..757afeb9e3 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -51,16 +51,20 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
if (bits_per_pixel == 1) {
// Requires bit unpacking...
- ERR_FAIL_COND_V(width % 8 != 0, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(height % 8 != 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V_MSG(width % 8 != 0, ERR_UNAVAILABLE,
+ vformat("1-bpp BMP images must have a width that is a multiple of 8, but the imported BMP is %d pixels wide.", int(width)));
+ ERR_FAIL_COND_V_MSG(height % 8 != 0, ERR_UNAVAILABLE,
+ vformat("1-bpp BMP images must have a height that is a multiple of 8, but the imported BMP is %d pixels tall.", int(height)));
} else if (bits_per_pixel == 4) {
// Requires bit unpacking...
- ERR_FAIL_COND_V(width % 2 != 0, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(height % 2 != 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V_MSG(width % 2 != 0, ERR_UNAVAILABLE,
+ vformat("4-bpp BMP images must have a width that is a multiple of 2, but the imported BMP is %d pixels wide.", int(width)));
+ ERR_FAIL_COND_V_MSG(height % 2 != 0, ERR_UNAVAILABLE,
+ vformat("4-bpp BMP images must have a height that is a multiple of 2, but the imported BMP is %d pixels tall.", int(height)));
} else if (bits_per_pixel == 16) {
- ERR_FAIL_V(ERR_UNAVAILABLE);
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "16-bpp BMP images are not supported.");
}
// Image data (might be indexed)
@@ -72,7 +76,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
} else { // color
data_len = width * height * 4;
}
- ERR_FAIL_COND_V(data_len == 0, ERR_BUG);
+ ERR_FAIL_COND_V_MSG(data_len == 0, ERR_BUG, "Couldn't parse the BMP image data.");
err = data.resize(data_len);
uint8_t *data_w = data.ptrw();
@@ -215,13 +219,15 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
// Info Header
bmp_header.bmp_info_header.bmp_header_size = f->get_32();
- ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V_MSG(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT,
+ vformat("Couldn't parse the BMP info header. The file is likely corrupt: %s", f->get_path()));
bmp_header.bmp_info_header.bmp_width = f->get_32();
bmp_header.bmp_info_header.bmp_height = f->get_32();
bmp_header.bmp_info_header.bmp_planes = f->get_16();
- ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V_MSG(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT,
+ vformat("Couldn't parse the BMP planes. The file is likely corrupt: %s", f->get_path()));
bmp_header.bmp_info_header.bmp_bit_count = f->get_16();
bmp_header.bmp_info_header.bmp_compression = f->get_32();
@@ -236,10 +242,10 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
case BI_RLE4:
case BI_CMYKRLE8:
case BI_CMYKRLE4: {
- // Stop parsing
- String bmp_path = f->get_path();
+ // Stop parsing.
f->close();
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Compressed BMP files are not supported: " + bmp_path + ".");
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE,
+ vformat("Compressed BMP files are not supported: %s", f->get_path()));
} break;
}
// Don't rely on sizeof(bmp_file_header) as structure padding
@@ -255,7 +261,8 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
if (bmp_header.bmp_info_header.bmp_bit_count <= 8) {
// Support 256 colors max
color_table_size = 1 << bmp_header.bmp_info_header.bmp_bit_count;
- ERR_FAIL_COND_V(color_table_size == 0, ERR_BUG);
+ ERR_FAIL_COND_V_MSG(color_table_size == 0, ERR_BUG,
+ vformat("Couldn't parse the BMP color table: %s", f->get_path()));
}
Vector<uint8_t> bmp_color_table;
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
index b853ebfc63..21bdcca18e 100644
--- a/modules/bullet/SCsub
+++ b/modules/bullet/SCsub
@@ -201,8 +201,8 @@ if env["builtin_bullet"]:
env_bullet.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
else:
env_bullet.Prepend(CPPPATH=[thirdparty_dir])
- # if env['target'] == "debug" or env['target'] == "release_debug":
- # env_bullet.Append(CPPDEFINES=['BT_DEBUG'])
+ if env["target"] == "debug" or env["target"] == "release_debug":
+ env_bullet.Append(CPPDEFINES=["DEBUG"])
env_bullet.Append(CPPDEFINES=["BT_USE_OLD_DAMPING_METHOD"])
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index 79d8e252f0..b35019bea3 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -65,14 +65,11 @@ AreaBullet::~AreaBullet() {
}
void AreaBullet::dispatch_callbacks() {
- if (!isScratched) {
- return;
- }
- isScratched = false;
+ RigidCollisionObjectBullet::dispatch_callbacks();
// Reverse order because I've to remove EXIT objects
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- OverlappingObjectData &otherObj = overlappingObjects.write[i];
+ OverlappingObjectData &otherObj = overlappingObjects[i];
switch (otherObj.state) {
case OVERLAP_STATE_ENTER:
@@ -112,10 +109,9 @@ void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3
}
void AreaBullet::scratch() {
- if (isScratched) {
- return;
+ if (space != nullptr) {
+ space->add_to_pre_flush_queue(this);
}
- isScratched = true;
}
void AreaBullet::clear_overlaps(bool p_notify) {
@@ -164,7 +160,7 @@ void AreaBullet::main_shape_changed() {
btGhost->setCollisionShape(get_main_shape());
}
-void AreaBullet::reload_body() {
+void AreaBullet::do_reload_body() {
if (space) {
space->remove_area(this);
space->add_area(this);
@@ -173,21 +169,25 @@ void AreaBullet::reload_body() {
void AreaBullet::set_space(SpaceBullet *p_space) {
// Clear the old space if there is one
+
if (space) {
- isScratched = false;
+ clear_overlaps(false);
// Remove this object form the physics world
+ space->unregister_collision_object(this);
space->remove_area(this);
}
space = p_space;
if (space) {
- space->add_area(this);
+ space->register_collision_object(this);
+ reload_body();
+ scratch();
}
}
-void AreaBullet::on_collision_filters_change() {
+void AreaBullet::do_reload_collision_filters() {
if (space) {
space->reload_collision_filters(this);
}
@@ -201,13 +201,13 @@ void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) {
void AreaBullet::put_overlap_as_exit(int p_index) {
scratch();
- overlappingObjects.write[p_index].state = OVERLAP_STATE_EXIT;
+ overlappingObjects[p_index].state = OVERLAP_STATE_EXIT;
}
void AreaBullet::put_overlap_as_inside(int p_index) {
// This check is required to be sure this body was inside
if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) {
- overlappingObjects.write[p_index].state = OVERLAP_STATE_INSIDE;
+ overlappingObjects[p_index].state = OVERLAP_STATE_INSIDE;
}
}
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index cde889c1ba..51fbc1f71d 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -32,7 +32,7 @@
#define AREABULLET_H
#include "collision_object_bullet.h"
-#include "core/vector.h"
+#include "core/local_vector.h"
#include "servers/physics_server_3d.h"
#include "space_bullet.h"
@@ -83,7 +83,7 @@ private:
Variant *call_event_res_ptr[5];
btGhostObject *btGhost;
- Vector<OverlappingObjectData> overlappingObjects;
+ LocalVector<OverlappingObjectData> overlappingObjects;
bool monitorable = true;
PhysicsServer3D::AreaSpaceOverrideMode spOv_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
@@ -93,11 +93,9 @@ private:
Vector3 spOv_gravityVec = Vector3(0, -1, 0);
real_t spOv_gravityMag = 10;
real_t spOv_linearDump = 0.1;
- real_t spOv_angularDump = 1;
+ real_t spOv_angularDump = 0.1;
int spOv_priority = 0;
- bool isScratched = false;
-
InOutEventCallback eventsCallbacks[2];
public:
@@ -139,11 +137,11 @@ public:
_FORCE_INLINE_ void set_spOv_priority(int p_priority) { spOv_priority = p_priority; }
_FORCE_INLINE_ int get_spOv_priority() { return spOv_priority; }
- virtual void main_shape_changed();
- virtual void reload_body();
- virtual void set_space(SpaceBullet *p_space);
+ virtual void main_shape_changed() override;
+ virtual void do_reload_body() override;
+ virtual void set_space(SpaceBullet *p_space) override;
- virtual void dispatch_callbacks();
+ virtual void dispatch_callbacks() override;
void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status);
void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
@@ -152,9 +150,9 @@ public:
// Dispatch the callbacks and removes from overlapping list
void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
- virtual void on_collision_filters_change();
- virtual void on_collision_checker_start() {}
- virtual void on_collision_checker_end() { isTransformChanged = false; }
+ virtual void do_reload_collision_filters() override;
+ virtual void on_collision_checker_start() override {}
+ virtual void on_collision_checker_end() override { isTransformChanged = false; }
void add_overlap(CollisionObjectBullet *p_otherObject);
void put_overlap_as_exit(int p_index);
@@ -166,8 +164,8 @@ public:
void set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method);
bool has_event_callback(Type p_callbackObjectType);
- virtual void on_enter_area(AreaBullet *p_area);
- virtual void on_exit_area(AreaBullet *p_area);
+ virtual void on_enter_area(AreaBullet *p_area) override;
+ virtual void on_exit_area(AreaBullet *p_area) override;
};
#endif
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 55686543f3..8f64c11867 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -561,14 +561,14 @@ void BulletPhysicsServer3D::body_clear_shapes(RID p_body) {
}
void BulletPhysicsServer3D::body_attach_object_instance_id(RID p_body, ObjectID p_id) {
- CollisionObjectBullet *body = get_collisin_object(p_body);
+ CollisionObjectBullet *body = get_collision_object(p_body);
ERR_FAIL_COND(!body);
body->set_instance_id(p_id);
}
ObjectID BulletPhysicsServer3D::body_get_object_instance_id(RID p_body) const {
- CollisionObjectBullet *body = get_collisin_object(p_body);
+ CollisionObjectBullet *body = get_collision_object(p_body);
ERR_FAIL_COND_V(!body, ObjectID());
return body->get_instance_id();
@@ -707,11 +707,11 @@ void BulletPhysicsServer3D::body_add_central_force(RID p_body, const Vector3 &p_
body->apply_central_force(p_force);
}
-void BulletPhysicsServer3D::body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos) {
+void BulletPhysicsServer3D::body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position) {
RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
ERR_FAIL_COND(!body);
- body->apply_force(p_force, p_pos);
+ body->apply_force(p_force, p_position);
}
void BulletPhysicsServer3D::body_add_torque(RID p_body, const Vector3 &p_torque) {
@@ -728,11 +728,11 @@ void BulletPhysicsServer3D::body_apply_central_impulse(RID p_body, const Vector3
body->apply_central_impulse(p_impulse);
}
-void BulletPhysicsServer3D::body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse) {
+void BulletPhysicsServer3D::body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position) {
RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
ERR_FAIL_COND(!body);
- body->apply_impulse(p_pos, p_impulse);
+ body->apply_impulse(p_impulse, p_position);
}
void BulletPhysicsServer3D::body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) {
@@ -1557,6 +1557,13 @@ void BulletPhysicsServer3D::sync() {
}
void BulletPhysicsServer3D::flush_queries() {
+ if (!active) {
+ return;
+ }
+
+ for (int i = 0; i < active_spaces_count; ++i) {
+ active_spaces[i]->flush_queries();
+ }
}
void BulletPhysicsServer3D::finish() {
@@ -1567,7 +1574,17 @@ int BulletPhysicsServer3D::get_process_info(ProcessInfo p_info) {
return 0;
}
-CollisionObjectBullet *BulletPhysicsServer3D::get_collisin_object(RID p_object) const {
+SpaceBullet *BulletPhysicsServer3D::get_space(RID p_rid) const {
+ ERR_FAIL_COND_V_MSG(space_owner.owns(p_rid) == false, nullptr, "The RID is not valid.");
+ return space_owner.getornull(p_rid);
+}
+
+ShapeBullet *BulletPhysicsServer3D::get_shape(RID p_rid) const {
+ ERR_FAIL_COND_V_MSG(shape_owner.owns(p_rid) == false, nullptr, "The RID is not valid.");
+ return shape_owner.getornull(p_rid);
+}
+
+CollisionObjectBullet *BulletPhysicsServer3D::get_collision_object(RID p_object) const {
if (rigid_body_owner.owns(p_object)) {
return rigid_body_owner.getornull(p_object);
}
@@ -1577,15 +1594,20 @@ CollisionObjectBullet *BulletPhysicsServer3D::get_collisin_object(RID p_object)
if (soft_body_owner.owns(p_object)) {
return soft_body_owner.getornull(p_object);
}
- return nullptr;
+ ERR_FAIL_V_MSG(nullptr, "The RID is no valid.");
}
-RigidCollisionObjectBullet *BulletPhysicsServer3D::get_rigid_collisin_object(RID p_object) const {
+RigidCollisionObjectBullet *BulletPhysicsServer3D::get_rigid_collision_object(RID p_object) const {
if (rigid_body_owner.owns(p_object)) {
return rigid_body_owner.getornull(p_object);
}
if (area_owner.owns(p_object)) {
return area_owner.getornull(p_object);
}
- return nullptr;
+ ERR_FAIL_V_MSG(nullptr, "The RID is no valid.");
+}
+
+JointBullet *BulletPhysicsServer3D::get_joint(RID p_rid) const {
+ ERR_FAIL_COND_V_MSG(joint_owner.owns(p_rid) == false, nullptr, "The RID is not valid.");
+ return joint_owner.getornull(p_rid);
}
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index 558d1ce5f7..eb95120f74 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -52,7 +52,7 @@ class BulletPhysicsServer3D : public PhysicsServer3D {
bool active = true;
char active_spaces_count = 0;
- Vector<SpaceBullet *> active_spaces;
+ LocalVector<SpaceBullet *> active_spaces;
mutable RID_PtrOwner<SpaceBullet> space_owner;
mutable RID_PtrOwner<ShapeBullet> shape_owner;
@@ -88,35 +88,35 @@ public:
}
/* SHAPE API */
- virtual RID shape_create(ShapeType p_shape);
- virtual void shape_set_data(RID p_shape, const Variant &p_data);
- virtual ShapeType shape_get_type(RID p_shape) const;
- virtual Variant shape_get_data(RID p_shape) const;
+ virtual RID shape_create(ShapeType p_shape) override;
+ virtual void shape_set_data(RID p_shape, const Variant &p_data) override;
+ virtual ShapeType shape_get_type(RID p_shape) const override;
+ virtual Variant shape_get_data(RID p_shape) const override;
- virtual void shape_set_margin(RID p_shape, real_t p_margin);
- virtual real_t shape_get_margin(RID p_shape) const;
+ virtual void shape_set_margin(RID p_shape, real_t p_margin) override;
+ virtual real_t shape_get_margin(RID p_shape) const override;
/// Not supported
- virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias);
+ virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias) override;
/// Not supported
- virtual real_t shape_get_custom_solver_bias(RID p_shape) const;
+ virtual real_t shape_get_custom_solver_bias(RID p_shape) const override;
/* SPACE API */
- virtual RID space_create();
- virtual void space_set_active(RID p_space, bool p_active);
- virtual bool space_is_active(RID p_space) const;
+ virtual RID space_create() override;
+ virtual void space_set_active(RID p_space, bool p_active) override;
+ virtual bool space_is_active(RID p_space) const override;
/// Not supported
- virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value);
+ virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) override;
/// Not supported
- virtual real_t space_get_param(RID p_space, SpaceParameter p_param) const;
+ virtual real_t space_get_param(RID p_space, SpaceParameter p_param) const override;
- virtual PhysicsDirectSpaceState3D *space_get_direct_state(RID p_space);
+ virtual PhysicsDirectSpaceState3D *space_get_direct_state(RID p_space) override;
- virtual void space_set_debug_contacts(RID p_space, int p_max_contacts);
- virtual Vector<Vector3> space_get_contacts(RID p_space) const;
- virtual int space_get_contact_count(RID p_space) const;
+ virtual void space_set_debug_contacts(RID p_space, int p_max_contacts) override;
+ virtual Vector<Vector3> space_get_contacts(RID p_space) const override;
+ virtual int space_get_contact_count(RID p_space) const override;
/* AREA API */
@@ -125,265 +125,265 @@ public:
/// The API area_set_param is a bit hacky, and allow Godot to set some parameters on Bullet's world, a different use print a warning to console.
/// All other APIs returns a warning message if used
- virtual RID area_create();
+ virtual RID area_create() override;
- virtual void area_set_space(RID p_area, RID p_space);
+ virtual void area_set_space(RID p_area, RID p_space) override;
- virtual RID area_get_space(RID p_area) const;
+ virtual RID area_get_space(RID p_area) const override;
- virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode);
- virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const;
+ virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) override;
+ virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const override;
- virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false);
- virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape);
- virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform);
- virtual int area_get_shape_count(RID p_area) const;
- virtual RID area_get_shape(RID p_area, int p_shape_idx) const;
- virtual Transform area_get_shape_transform(RID p_area, int p_shape_idx) const;
- virtual void area_remove_shape(RID p_area, int p_shape_idx);
- virtual void area_clear_shapes(RID p_area);
- virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled);
- virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id);
- virtual ObjectID area_get_object_instance_id(RID p_area) const;
+ virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false) override;
+ virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape) override;
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform) override;
+ virtual int area_get_shape_count(RID p_area) const override;
+ virtual RID area_get_shape(RID p_area, int p_shape_idx) const override;
+ virtual Transform area_get_shape_transform(RID p_area, int p_shape_idx) const override;
+ virtual void area_remove_shape(RID p_area, int p_shape_idx) override;
+ virtual void area_clear_shapes(RID p_area) override;
+ virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) override;
+ virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id) override;
+ virtual ObjectID area_get_object_instance_id(RID p_area) const override;
/// If you pass as p_area the SpaceBullet you can set some parameters as specified below
/// AREA_PARAM_GRAVITY
/// AREA_PARAM_GRAVITY_VECTOR
/// Otherwise you can set area parameters
- virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value);
- virtual Variant area_get_param(RID p_area, AreaParameter p_param) const;
+ virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) override;
+ virtual Variant area_get_param(RID p_area, AreaParameter p_param) const override;
- virtual void area_set_transform(RID p_area, const Transform &p_transform);
- virtual Transform area_get_transform(RID p_area) const;
+ virtual void area_set_transform(RID p_area, const Transform &p_transform) override;
+ virtual Transform area_get_transform(RID p_area) const override;
- virtual void area_set_collision_mask(RID p_area, uint32_t p_mask);
- virtual void area_set_collision_layer(RID p_area, uint32_t p_layer);
+ virtual void area_set_collision_mask(RID p_area, uint32_t p_mask) override;
+ virtual void area_set_collision_layer(RID p_area, uint32_t p_layer) override;
- virtual void area_set_monitorable(RID p_area, bool p_monitorable);
- virtual void area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method);
- virtual void area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method);
- virtual void area_set_ray_pickable(RID p_area, bool p_enable);
- virtual bool area_is_ray_pickable(RID p_area) const;
+ virtual void area_set_monitorable(RID p_area, bool p_monitorable) override;
+ virtual void area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) override;
+ virtual void area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) override;
+ virtual void area_set_ray_pickable(RID p_area, bool p_enable) override;
+ virtual bool area_is_ray_pickable(RID p_area) const override;
/* RIGID BODY API */
- virtual RID body_create(BodyMode p_mode = BODY_MODE_RIGID, bool p_init_sleeping = false);
+ virtual RID body_create(BodyMode p_mode = BODY_MODE_RIGID, bool p_init_sleeping = false) override;
- virtual void body_set_space(RID p_body, RID p_space);
- virtual RID body_get_space(RID p_body) const;
+ virtual void body_set_space(RID p_body, RID p_space) override;
+ virtual RID body_get_space(RID p_body) const override;
- virtual void body_set_mode(RID p_body, BodyMode p_mode);
- virtual BodyMode body_get_mode(RID p_body) const;
+ virtual void body_set_mode(RID p_body, BodyMode p_mode) override;
+ virtual BodyMode body_get_mode(RID p_body) const override;
- virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false);
+ virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false) override;
// Not supported, Please remove and add new shape
- virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape);
- virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform &p_transform);
+ virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape) override;
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform &p_transform) override;
- virtual int body_get_shape_count(RID p_body) const;
- virtual RID body_get_shape(RID p_body, int p_shape_idx) const;
- virtual Transform body_get_shape_transform(RID p_body, int p_shape_idx) const;
+ virtual int body_get_shape_count(RID p_body) const override;
+ virtual RID body_get_shape(RID p_body, int p_shape_idx) const override;
+ virtual Transform body_get_shape_transform(RID p_body, int p_shape_idx) const override;
- virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled);
+ virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) override;
- virtual void body_remove_shape(RID p_body, int p_shape_idx);
- virtual void body_clear_shapes(RID p_body);
+ virtual void body_remove_shape(RID p_body, int p_shape_idx) override;
+ virtual void body_clear_shapes(RID p_body) override;
// Used for Rigid and Soft Bodies
- virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id);
- virtual ObjectID body_get_object_instance_id(RID p_body) const;
+ virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) override;
+ virtual ObjectID body_get_object_instance_id(RID p_body) const override;
- virtual void body_set_enable_continuous_collision_detection(RID p_body, bool p_enable);
- virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const;
+ virtual void body_set_enable_continuous_collision_detection(RID p_body, bool p_enable) override;
+ virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const override;
- virtual void body_set_collision_layer(RID p_body, uint32_t p_layer);
- virtual uint32_t body_get_collision_layer(RID p_body) const;
+ virtual void body_set_collision_layer(RID p_body, uint32_t p_layer) override;
+ virtual uint32_t body_get_collision_layer(RID p_body) const override;
- virtual void body_set_collision_mask(RID p_body, uint32_t p_mask);
- virtual uint32_t body_get_collision_mask(RID p_body) const;
+ virtual void body_set_collision_mask(RID p_body, uint32_t p_mask) override;
+ virtual uint32_t body_get_collision_mask(RID p_body) const override;
/// This is not supported by physics server
- virtual void body_set_user_flags(RID p_body, uint32_t p_flags);
+ virtual void body_set_user_flags(RID p_body, uint32_t p_flags) override;
/// This is not supported by physics server
- virtual uint32_t body_get_user_flags(RID p_body) const;
+ virtual uint32_t body_get_user_flags(RID p_body) const override;
- virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value);
- virtual float body_get_param(RID p_body, BodyParameter p_param) const;
+ virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value) override;
+ virtual float body_get_param(RID p_body, BodyParameter p_param) const override;
- virtual void body_set_kinematic_safe_margin(RID p_body, real_t p_margin);
- virtual real_t body_get_kinematic_safe_margin(RID p_body) const;
+ virtual void body_set_kinematic_safe_margin(RID p_body, real_t p_margin) override;
+ virtual real_t body_get_kinematic_safe_margin(RID p_body) const override;
- virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
- virtual Variant body_get_state(RID p_body, BodyState p_state) const;
+ virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override;
+ virtual Variant body_get_state(RID p_body, BodyState p_state) const override;
- virtual void body_set_applied_force(RID p_body, const Vector3 &p_force);
- virtual Vector3 body_get_applied_force(RID p_body) const;
+ virtual void body_set_applied_force(RID p_body, const Vector3 &p_force) override;
+ virtual Vector3 body_get_applied_force(RID p_body) const override;
- virtual void body_set_applied_torque(RID p_body, const Vector3 &p_torque);
- virtual Vector3 body_get_applied_torque(RID p_body) const;
+ virtual void body_set_applied_torque(RID p_body, const Vector3 &p_torque) override;
+ virtual Vector3 body_get_applied_torque(RID p_body) const override;
- virtual void body_add_central_force(RID p_body, const Vector3 &p_force);
- virtual void body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos);
- virtual void body_add_torque(RID p_body, const Vector3 &p_torque);
+ virtual void body_add_central_force(RID p_body, const Vector3 &p_force) override;
+ virtual void body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position = Vector3()) override;
+ virtual void body_add_torque(RID p_body, const Vector3 &p_torque) override;
- virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse);
- virtual void body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse);
- virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse);
- virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity);
+ virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override;
+ virtual void body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override;
+ virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) override;
+ virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity) override;
- virtual void body_set_axis_lock(RID p_body, BodyAxis p_axis, bool p_lock);
- virtual bool body_is_axis_locked(RID p_body, BodyAxis p_axis) const;
+ virtual void body_set_axis_lock(RID p_body, BodyAxis p_axis, bool p_lock) override;
+ virtual bool body_is_axis_locked(RID p_body, BodyAxis p_axis) const override;
- virtual void body_add_collision_exception(RID p_body, RID p_body_b);
- virtual void body_remove_collision_exception(RID p_body, RID p_body_b);
- virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions);
+ virtual void body_add_collision_exception(RID p_body, RID p_body_b) override;
+ virtual void body_remove_collision_exception(RID p_body, RID p_body_b) override;
+ virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override;
- virtual void body_set_max_contacts_reported(RID p_body, int p_contacts);
- virtual int body_get_max_contacts_reported(RID p_body) const;
+ virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override;
+ virtual int body_get_max_contacts_reported(RID p_body) const override;
- virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold);
- virtual float body_get_contacts_reported_depth_threshold(RID p_body) const;
+ virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold) override;
+ virtual float body_get_contacts_reported_depth_threshold(RID p_body) const override;
- virtual void body_set_omit_force_integration(RID p_body, bool p_omit);
- virtual bool body_is_omitting_force_integration(RID p_body) const;
+ virtual void body_set_omit_force_integration(RID p_body, bool p_omit) override;
+ virtual bool body_is_omitting_force_integration(RID p_body) const override;
- virtual void body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata = Variant());
+ virtual void body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata = Variant()) override;
- virtual void body_set_ray_pickable(RID p_body, bool p_enable);
- virtual bool body_is_ray_pickable(RID p_body) const;
+ virtual void body_set_ray_pickable(RID p_body, bool p_enable) override;
+ virtual bool body_is_ray_pickable(RID p_body) const override;
// this function only works on physics process, errors and returns null otherwise
- virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body);
+ virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override;
- virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true);
- virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001);
+ virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override;
+ virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) override;
/* SOFT BODY API */
- virtual RID soft_body_create(bool p_init_sleeping = false);
+ virtual RID soft_body_create(bool p_init_sleeping = false) override;
- virtual void soft_body_update_rendering_server(RID p_body, class SoftBodyRenderingServerHandler *p_rendering_server_handler);
+ virtual void soft_body_update_rendering_server(RID p_body, class SoftBodyRenderingServerHandler *p_rendering_server_handler) override;
- virtual void soft_body_set_space(RID p_body, RID p_space);
- virtual RID soft_body_get_space(RID p_body) const;
+ virtual void soft_body_set_space(RID p_body, RID p_space) override;
+ virtual RID soft_body_get_space(RID p_body) const override;
- virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh);
+ virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) override;
- virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer);
- virtual uint32_t soft_body_get_collision_layer(RID p_body) const;
+ virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) override;
+ virtual uint32_t soft_body_get_collision_layer(RID p_body) const override;
- virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask);
- virtual uint32_t soft_body_get_collision_mask(RID p_body) const;
+ virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) override;
+ virtual uint32_t soft_body_get_collision_mask(RID p_body) const override;
- virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b);
- virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b);
- virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions);
+ virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) override;
+ virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) override;
+ virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override;
- virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant);
- virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const;
+ virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override;
+ virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const override;
/// Special function. This function has bad performance
- virtual void soft_body_set_transform(RID p_body, const Transform &p_transform);
- virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const;
+ virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) override;
+ virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const override;
- virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable);
- virtual bool soft_body_is_ray_pickable(RID p_body) const;
+ virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) override;
+ virtual bool soft_body_is_ray_pickable(RID p_body) const override;
- virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision);
- virtual int soft_body_get_simulation_precision(RID p_body);
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) override;
+ virtual int soft_body_get_simulation_precision(RID p_body) override;
- virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass);
- virtual real_t soft_body_get_total_mass(RID p_body);
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) override;
+ virtual real_t soft_body_get_total_mass(RID p_body) override;
- virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness);
- virtual real_t soft_body_get_linear_stiffness(RID p_body);
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) override;
+ virtual real_t soft_body_get_linear_stiffness(RID p_body) override;
- virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness);
- virtual real_t soft_body_get_areaAngular_stiffness(RID p_body);
+ virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) override;
+ virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) override;
- virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness);
- virtual real_t soft_body_get_volume_stiffness(RID p_body);
+ virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) override;
+ virtual real_t soft_body_get_volume_stiffness(RID p_body) override;
- virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient);
- virtual real_t soft_body_get_pressure_coefficient(RID p_body);
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) override;
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body) override;
- virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient);
- virtual real_t soft_body_get_pose_matching_coefficient(RID p_body);
+ virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) override;
+ virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) override;
- virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient);
- virtual real_t soft_body_get_damping_coefficient(RID p_body);
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) override;
+ virtual real_t soft_body_get_damping_coefficient(RID p_body) override;
- virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient);
- virtual real_t soft_body_get_drag_coefficient(RID p_body);
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) override;
+ virtual real_t soft_body_get_drag_coefficient(RID p_body) override;
- virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position);
- virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index);
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) override;
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) override;
- virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const;
+ virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const override;
- virtual void soft_body_remove_all_pinned_points(RID p_body);
- virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin);
- virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index);
+ virtual void soft_body_remove_all_pinned_points(RID p_body) override;
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) override;
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) override;
/* JOINT API */
- virtual JointType joint_get_type(RID p_joint) const;
+ virtual JointType joint_get_type(RID p_joint) const override;
- virtual void joint_set_solver_priority(RID p_joint, int p_priority);
- virtual int joint_get_solver_priority(RID p_joint) const;
+ virtual void joint_set_solver_priority(RID p_joint, int p_priority) override;
+ virtual int joint_get_solver_priority(RID p_joint) const override;
- virtual void joint_disable_collisions_between_bodies(RID p_joint, const bool p_disable);
- virtual bool joint_is_disabled_collisions_between_bodies(RID p_joint) const;
+ virtual void joint_disable_collisions_between_bodies(RID p_joint, const bool p_disable) override;
+ virtual bool joint_is_disabled_collisions_between_bodies(RID p_joint) const override;
- virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B);
+ virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B) override;
- virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, float p_value);
- virtual float pin_joint_get_param(RID p_joint, PinJointParam p_param) const;
+ virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, float p_value) override;
+ virtual float pin_joint_get_param(RID p_joint, PinJointParam p_param) const override;
- virtual void pin_joint_set_local_a(RID p_joint, const Vector3 &p_A);
- virtual Vector3 pin_joint_get_local_a(RID p_joint) const;
+ virtual void pin_joint_set_local_a(RID p_joint, const Vector3 &p_A) override;
+ virtual Vector3 pin_joint_get_local_a(RID p_joint) const override;
- virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B);
- virtual Vector3 pin_joint_get_local_b(RID p_joint) const;
+ virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B) override;
+ virtual Vector3 pin_joint_get_local_b(RID p_joint) const override;
- virtual RID joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B);
- virtual RID joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B);
+ virtual RID joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B) override;
+ virtual RID joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B) override;
- virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value);
- virtual float hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const;
+ virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value) override;
+ virtual float hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const override;
- virtual void hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_value);
- virtual bool hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const;
+ virtual void hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_value) override;
+ virtual bool hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const override;
/// Reference frame is A
- virtual RID joint_create_slider(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B);
+ virtual RID joint_create_slider(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) override;
- virtual void slider_joint_set_param(RID p_joint, SliderJointParam p_param, float p_value);
- virtual float slider_joint_get_param(RID p_joint, SliderJointParam p_param) const;
+ virtual void slider_joint_set_param(RID p_joint, SliderJointParam p_param, float p_value) override;
+ virtual float slider_joint_get_param(RID p_joint, SliderJointParam p_param) const override;
/// Reference frame is A
- virtual RID joint_create_cone_twist(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B);
+ virtual RID joint_create_cone_twist(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) override;
- virtual void cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, float p_value);
- virtual float cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const;
+ virtual void cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, float p_value) override;
+ virtual float cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const override;
/// Reference frame is A
- virtual RID joint_create_generic_6dof(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B);
+ virtual RID joint_create_generic_6dof(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) override;
- virtual void generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, float p_value);
- virtual float generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param);
+ virtual void generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, float p_value) override;
+ virtual float generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param) override;
- virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable);
- virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag);
+ virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable) override;
+ virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag) override;
- virtual void generic_6dof_joint_set_precision(RID p_joint, int precision);
- virtual int generic_6dof_joint_get_precision(RID p_joint);
+ virtual void generic_6dof_joint_set_precision(RID p_joint, int precision) override;
+ virtual int generic_6dof_joint_get_precision(RID p_joint) override;
/* MISC */
- virtual void free(RID p_rid);
+ virtual void free(RID p_rid) override;
- virtual void set_active(bool p_active) {
+ virtual void set_active(bool p_active) override {
active = p_active;
}
@@ -395,21 +395,21 @@ public:
return active;
}
- virtual void init();
- virtual void step(float p_deltaTime);
- virtual void sync();
- virtual void flush_queries();
- virtual void finish();
+ virtual void init() override;
+ virtual void step(float p_deltaTime) override;
+ virtual void sync() override;
+ virtual void flush_queries() override;
+ virtual void finish() override;
- virtual bool is_flushing_queries() const { return false; }
+ virtual bool is_flushing_queries() const override { return false; }
- virtual int get_process_info(ProcessInfo p_info);
+ virtual int get_process_info(ProcessInfo p_info) override;
- CollisionObjectBullet *get_collisin_object(RID p_object) const;
- RigidCollisionObjectBullet *get_rigid_collisin_object(RID p_object) const;
-
- /// Internal APIs
-public:
+ SpaceBullet *get_space(RID p_rid) const;
+ ShapeBullet *get_shape(RID p_rid) const;
+ CollisionObjectBullet *get_collision_object(RID p_object) const;
+ RigidCollisionObjectBullet *get_rigid_collision_object(RID p_object) const;
+ JointBullet *get_joint(RID p_rid) const;
};
#endif
diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp
index c9493d8892..7ecad9b78a 100644
--- a/modules/bullet/bullet_types_converter.cpp
+++ b/modules/bullet/bullet_types_converter.cpp
@@ -95,12 +95,61 @@ void G_TO_B(Transform const &inVal, btTransform &outVal) {
}
void UNSCALE_BT_BASIS(btTransform &scaledBasis) {
- btMatrix3x3 &m(scaledBasis.getBasis());
- btVector3 column0(m[0][0], m[1][0], m[2][0]);
- btVector3 column1(m[0][1], m[1][1], m[2][1]);
- btVector3 column2(m[0][2], m[1][2], m[2][2]);
+ btMatrix3x3 &basis(scaledBasis.getBasis());
+ btVector3 column0 = basis.getColumn(0);
+ btVector3 column1 = basis.getColumn(1);
+ btVector3 column2 = basis.getColumn(2);
+
+ // Check for zero scaling.
+ if (btFuzzyZero(column0[0])) {
+ if (btFuzzyZero(column1[1])) {
+ if (btFuzzyZero(column2[2])) {
+ // All dimensions are fuzzy zero. Create a default basis.
+ column0 = btVector3(1, 0, 0);
+ column1 = btVector3(0, 1, 0);
+ column2 = btVector3(0, 0, 1);
+ } else { // Column 2 scale not fuzzy zero.
+ // Create two vectors orthogonal to row 2.
+ // Ensure that a default basis is created if row 2 = <0, 0, 1>
+ column1 = btVector3(0, column2[2], -column2[1]);
+ column0 = column1.cross(column2);
+ }
+ } else { // Column 1 scale not fuzzy zero.
+ if (btFuzzyZero(column2[2])) {
+ // Create two vectors othogonal to column 1.
+ // Ensure that a default basis is created if column 1 = <0, 1, 0>
+ column0 = btVector3(column1[1], -column1[0], 0);
+ column2 = column0.cross(column1);
+ } else { // Column 1 and column 2 scales not fuzzy zero.
+ // Create column 0 orthogonal to column 1 and column 2.
+ column0 = column1.cross(column2);
+ }
+ }
+ } else { // Column 0 scale not fuzzy zero.
+ if (btFuzzyZero(column1[1])) {
+ if (btFuzzyZero(column2[2])) {
+ // Create two vectors orthogonal to column 0.
+ // Ensure that a default basis is created if column 0 = <1, 0, 0>
+ column2 = btVector3(-column0[2], 0, column0[0]);
+ column1 = column2.cross(column0);
+ } else { // Column 0 and column 2 scales not fuzzy zero.
+ // Create column 1 orthogonal to column 0 and column 2.
+ column1 = column2.cross(column0);
+ }
+ } else { // Column 0 and column 1 scales not fuzzy zero.
+ if (btFuzzyZero(column2[2])) {
+ // Create column 2 orthogonal to column 0 and column 1.
+ column2 = column0.cross(column1);
+ }
+ }
+ }
+
+ // Normalize
column0.normalize();
column1.normalize();
column2.normalize();
- m.setValue(column0[0], column1[0], column2[0], column0[1], column1[1], column2[1], column0[2], column1[2], column2[2]);
+
+ basis.setValue(column0[0], column1[0], column2[0],
+ column0[1], column1[1], column2[1],
+ column0[2], column1[2], column2[2]);
}
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index a3158a15e5..660e9afc5e 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -79,7 +79,7 @@ btTransform CollisionObjectBullet::ShapeWrapper::get_adjusted_transform() const
}
void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_scale) {
- if (!bt_shape) {
+ if (bt_shape == nullptr) {
if (active) {
bt_shape = shape->create_bt_shape(scale * body_scale);
} else {
@@ -88,6 +88,13 @@ void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_s
}
}
+void CollisionObjectBullet::ShapeWrapper::release_bt_shape() {
+ if (bt_shape != nullptr) {
+ shape->destroy_bt_shape(bt_shape);
+ bt_shape = nullptr;
+ }
+}
+
CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
RIDBullet(),
type(p_type) {}
@@ -158,6 +165,22 @@ bool CollisionObjectBullet::has_collision_exception(const CollisionObjectBullet
return !bt_collision_object->checkCollideWith(p_otherCollisionObject->bt_collision_object);
}
+void CollisionObjectBullet::reload_body() {
+ needs_body_reload = true;
+}
+
+void CollisionObjectBullet::dispatch_callbacks() {}
+
+void CollisionObjectBullet::pre_process() {
+ if (needs_body_reload) {
+ do_reload_body();
+ } else if (needs_collision_filters_reload) {
+ do_reload_collision_filters();
+ }
+ needs_body_reload = false;
+ needs_collision_filters_reload = false;
+}
+
void CollisionObjectBullet::set_collision_enabled(bool p_enabled) {
collisionsEnabled = p_enabled;
if (collisionsEnabled) {
@@ -231,7 +254,7 @@ void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform
}
void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) {
- ShapeWrapper &shp = shapes.write[p_index];
+ ShapeWrapper &shp = shapes[p_index];
shp.shape->remove_owner(this);
p_shape->add_owner(this);
shp.shape = p_shape;
@@ -293,7 +316,7 @@ void RigidCollisionObjectBullet::remove_all_shapes(bool p_permanentlyFromThisBod
void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) {
ERR_FAIL_INDEX(p_index, get_shape_count());
- shapes.write[p_index].set_transform(p_transform);
+ shapes[p_index].set_transform(p_transform);
shape_changed(p_index);
}
@@ -311,7 +334,7 @@ void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled
if (shapes[p_index].active != p_disabled) {
return;
}
- shapes.write[p_index].active = !p_disabled;
+ shapes[p_index].active = !p_disabled;
shape_changed(p_index);
}
@@ -319,16 +342,28 @@ bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) {
return !shapes[p_index].active;
}
+void RigidCollisionObjectBullet::pre_process() {
+ if (need_shape_reload) {
+ do_reload_shapes();
+ need_shape_reload = false;
+ }
+ CollisionObjectBullet::pre_process();
+}
+
void RigidCollisionObjectBullet::shape_changed(int p_shape_index) {
- ShapeWrapper &shp = shapes.write[p_shape_index];
+ ShapeWrapper &shp = shapes[p_shape_index];
if (shp.bt_shape == mainShape) {
mainShape = nullptr;
}
- bulletdelete(shp.bt_shape);
+ shp.release_bt_shape();
reload_shapes();
}
void RigidCollisionObjectBullet::reload_shapes() {
+ need_shape_reload = true;
+}
+
+void RigidCollisionObjectBullet::do_reload_shapes() {
if (mainShape && mainShape->isCompound()) {
// Destroy compound
bulletdelete(mainShape);
@@ -336,41 +371,38 @@ void RigidCollisionObjectBullet::reload_shapes() {
mainShape = nullptr;
- ShapeWrapper *shpWrapper;
const int shape_count = shapes.size();
- // Reset shape if required
+ // Reset all shapes if required
if (force_shape_reset) {
for (int i(0); i < shape_count; ++i) {
- shpWrapper = &shapes.write[i];
- bulletdelete(shpWrapper->bt_shape);
+ shapes[i].release_bt_shape();
}
force_shape_reset = false;
}
const btVector3 body_scale(get_bt_body_scale());
- // Try to optimize by not using compound
if (1 == shape_count) {
- shpWrapper = &shapes.write[0];
- btTransform transform = shpWrapper->get_adjusted_transform();
+ // Is it possible to optimize by not using compound?
+ btTransform transform = shapes[0].get_adjusted_transform();
if (transform.getOrigin().isZero() && transform.getBasis() == transform.getBasis().getIdentity()) {
- shpWrapper->claim_bt_shape(body_scale);
- mainShape = shpWrapper->bt_shape;
+ shapes[0].claim_bt_shape(body_scale);
+ mainShape = shapes[0].bt_shape;
main_shape_changed();
+ // Nothing more to do
return;
}
}
- // Optimization not possible use a compound shape
+ // Optimization not possible use a compound shape.
btCompoundShape *compoundShape = bulletnew(btCompoundShape(enableDynamicAabbTree, shape_count));
for (int i(0); i < shape_count; ++i) {
- shpWrapper = &shapes.write[i];
- shpWrapper->claim_bt_shape(body_scale);
- btTransform scaled_shape_transform(shpWrapper->get_adjusted_transform());
+ shapes[i].claim_bt_shape(body_scale);
+ btTransform scaled_shape_transform(shapes[i].get_adjusted_transform());
scaled_shape_transform.getOrigin() *= body_scale;
- compoundShape->addChildShape(scaled_shape_transform, shpWrapper->bt_shape);
+ compoundShape->addChildShape(scaled_shape_transform, shapes[i].bt_shape);
}
compoundShape->recalculateLocalAabb();
@@ -384,10 +416,10 @@ void RigidCollisionObjectBullet::body_scale_changed() {
}
void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) {
- ShapeWrapper &shp = shapes.write[p_index];
+ ShapeWrapper &shp = shapes[p_index];
shp.shape->remove_owner(this, p_permanentlyFromThisBody);
if (shp.bt_shape == mainShape) {
mainShape = nullptr;
}
- bulletdelete(shp.bt_shape);
+ shp.release_bt_shape();
}
diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h
index f1423a69e4..920d80af23 100644
--- a/modules/bullet/collision_object_bullet.h
+++ b/modules/bullet/collision_object_bullet.h
@@ -31,6 +31,7 @@
#ifndef COLLISION_OBJECT_BULLET_H
#define COLLISION_OBJECT_BULLET_H
+#include "core/local_vector.h"
#include "core/math/transform.h"
#include "core/math/vector3.h"
#include "core/object.h"
@@ -70,11 +71,12 @@ public:
struct ShapeWrapper {
ShapeBullet *shape = nullptr;
- btCollisionShape *bt_shape = nullptr;
btTransform transform;
btVector3 scale;
bool active = true;
+ btCollisionShape *bt_shape = nullptr;
+ public:
ShapeWrapper() {}
ShapeWrapper(ShapeBullet *p_shape, const btTransform &p_transform, bool p_active) :
@@ -107,6 +109,7 @@ public:
btTransform get_adjusted_transform() const;
void claim_bt_shape(const btVector3 &body_scale);
+ void release_bt_shape();
};
protected:
@@ -124,13 +127,20 @@ protected:
VSet<RID> exceptions;
+ bool needs_body_reload = true;
+ bool needs_collision_filters_reload = true;
+
/// This array is used to know all areas where this Object is overlapped in
/// New area is added when overlap with new area (AreaBullet::addOverlap), then is removed when it exit (CollisionObjectBullet::onExitArea)
/// This array is used mainly to know which area hold the pointer of this object
- Vector<AreaBullet *> areasOverlapped;
+ LocalVector<AreaBullet *> areasOverlapped;
bool isTransformChanged = false;
public:
+ bool is_in_world = false;
+ bool is_in_flush_queue = false;
+
+public:
CollisionObjectBullet(Type p_type);
virtual ~CollisionObjectBullet();
@@ -164,7 +174,7 @@ public:
_FORCE_INLINE_ void set_collision_layer(uint32_t p_layer) {
if (collisionLayer != p_layer) {
collisionLayer = p_layer;
- on_collision_filters_change();
+ needs_collision_filters_reload = true;
}
}
_FORCE_INLINE_ uint32_t get_collision_layer() const { return collisionLayer; }
@@ -172,25 +182,32 @@ public:
_FORCE_INLINE_ void set_collision_mask(uint32_t p_mask) {
if (collisionMask != p_mask) {
collisionMask = p_mask;
- on_collision_filters_change();
+ needs_collision_filters_reload = true;
}
}
_FORCE_INLINE_ uint32_t get_collision_mask() const { return collisionMask; }
- virtual void on_collision_filters_change() = 0;
+ virtual void do_reload_collision_filters() = 0;
_FORCE_INLINE_ bool test_collision_mask(CollisionObjectBullet *p_other) const {
return collisionLayer & p_other->collisionMask || p_other->collisionLayer & collisionMask;
}
- virtual void reload_body() = 0;
+ bool need_reload_body() const {
+ return needs_body_reload;
+ }
+
+ void reload_body();
+
+ virtual void do_reload_body() = 0;
virtual void set_space(SpaceBullet *p_space) = 0;
_FORCE_INLINE_ SpaceBullet *get_space() const { return space; }
virtual void on_collision_checker_start() = 0;
virtual void on_collision_checker_end() = 0;
- virtual void dispatch_callbacks() = 0;
+ virtual void dispatch_callbacks();
+ virtual void pre_process();
void set_collision_enabled(bool p_enabled);
bool is_collisions_response_enabled();
@@ -214,14 +231,15 @@ public:
class RigidCollisionObjectBullet : public CollisionObjectBullet, public ShapeOwnerBullet {
protected:
btCollisionShape *mainShape = nullptr;
- Vector<ShapeWrapper> shapes;
+ LocalVector<ShapeWrapper> shapes;
+ bool need_shape_reload = true;
public:
RigidCollisionObjectBullet(Type p_type) :
CollisionObjectBullet(p_type) {}
~RigidCollisionObjectBullet();
- _FORCE_INLINE_ const Vector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; }
+ _FORCE_INLINE_ const LocalVector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; }
_FORCE_INLINE_ btCollisionShape *get_main_shape() const { return mainShape; }
@@ -232,9 +250,9 @@ public:
ShapeBullet *get_shape(int p_index) const;
btCollisionShape *get_bt_shape(int p_index) const;
- int find_shape(ShapeBullet *p_shape) const;
+ virtual int find_shape(ShapeBullet *p_shape) const override;
- virtual void remove_shape_full(ShapeBullet *p_shape);
+ virtual void remove_shape_full(ShapeBullet *p_shape) override;
void remove_shape_full(int p_index);
void remove_all_shapes(bool p_permanentlyFromThisBody = false, bool p_force_not_reload = false);
@@ -246,11 +264,15 @@ public:
void set_shape_disabled(int p_index, bool p_disabled);
bool is_shape_disabled(int p_index);
- virtual void shape_changed(int p_shape_index);
- virtual void reload_shapes();
+ virtual void pre_process() override;
+
+ virtual void shape_changed(int p_shape_index) override;
+ virtual void reload_shapes() override;
+ bool need_reload_shapes() const { return need_shape_reload; }
+ virtual void do_reload_shapes();
virtual void main_shape_changed() = 0;
- virtual void body_scale_changed();
+ virtual void body_scale_changed() override;
private:
void internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody = false);
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 2fca7fe633..f517eecf64 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -51,9 +51,7 @@
BulletPhysicsDirectBodyState3D *BulletPhysicsDirectBodyState3D::singleton = nullptr;
Vector3 BulletPhysicsDirectBodyState3D::get_total_gravity() const {
- Vector3 gVec;
- B_TO_G(body->btBody->getGravity(), gVec);
- return gVec;
+ return body->total_gravity;
}
float BulletPhysicsDirectBodyState3D::get_total_angular_damp() const {
@@ -118,8 +116,8 @@ void BulletPhysicsDirectBodyState3D::add_central_force(const Vector3 &p_force) {
body->apply_central_force(p_force);
}
-void BulletPhysicsDirectBodyState3D::add_force(const Vector3 &p_force, const Vector3 &p_pos) {
- body->apply_force(p_force, p_pos);
+void BulletPhysicsDirectBodyState3D::add_force(const Vector3 &p_force, const Vector3 &p_position) {
+ body->apply_force(p_force, p_position);
}
void BulletPhysicsDirectBodyState3D::add_torque(const Vector3 &p_torque) {
@@ -130,8 +128,8 @@ void BulletPhysicsDirectBodyState3D::apply_central_impulse(const Vector3 &p_impu
body->apply_central_impulse(p_impulse);
}
-void BulletPhysicsDirectBodyState3D::apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse) {
- body->apply_impulse(p_pos, p_impulse);
+void BulletPhysicsDirectBodyState3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) {
+ body->apply_impulse(p_impulse, p_position);
}
void BulletPhysicsDirectBodyState3D::apply_torque_impulse(const Vector3 &p_impulse) {
@@ -183,7 +181,7 @@ int BulletPhysicsDirectBodyState3D::get_contact_collider_shape(int p_contact_idx
}
Vector3 BulletPhysicsDirectBodyState3D::get_contact_collider_velocity_at_position(int p_contact_idx) const {
- RigidBodyBullet::CollisionData &colDat = body->collisions.write[p_contact_idx];
+ RigidBodyBullet::CollisionData &colDat = body->collisions[p_contact_idx];
btVector3 hitLocation;
G_TO_B(colDat.hitLocalLocation, hitLocation);
@@ -213,7 +211,7 @@ void RigidBodyBullet::KinematicUtilities::setSafeMargin(btScalar p_margin) {
}
void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
- const Vector<CollisionObjectBullet::ShapeWrapper> &shapes_wrappers(owner->get_shapes_wrappers());
+ const LocalVector<CollisionObjectBullet::ShapeWrapper> &shapes_wrappers(owner->get_shapes_wrappers());
const int shapes_count = shapes_wrappers.size();
just_delete_shapes(shapes_count);
@@ -228,8 +226,8 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
continue;
}
- shapes.write[i].transform = shape_wrapper->transform;
- shapes.write[i].transform.getOrigin() *= owner_scale;
+ shapes[i].transform = shape_wrapper->transform;
+ shapes[i].transform.getOrigin() *= owner_scale;
switch (shape_wrapper->shape->get_type()) {
case PhysicsServer3D::SHAPE_SPHERE:
case PhysicsServer3D::SHAPE_BOX:
@@ -237,11 +235,11 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
case PhysicsServer3D::SHAPE_CYLINDER:
case PhysicsServer3D::SHAPE_CONVEX_POLYGON:
case PhysicsServer3D::SHAPE_RAY: {
- shapes.write[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
+ shapes[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->internal_create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
} break;
default:
WARN_PRINT("This shape is not supported for kinematic collision.");
- shapes.write[i].shape = nullptr;
+ shapes[i].shape = nullptr;
}
}
}
@@ -249,7 +247,7 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
void RigidBodyBullet::KinematicUtilities::just_delete_shapes(int new_size) {
for (int i = shapes.size() - 1; 0 <= i; --i) {
if (shapes[i].shape) {
- bulletdelete(shapes.write[i].shape);
+ bulletdelete(shapes[i].shape);
}
}
shapes.resize(new_size);
@@ -271,8 +269,8 @@ RigidBodyBullet::RigidBodyBullet() :
reload_axis_lock();
areasWhereIam.resize(maxAreasWhereIam);
- for (int i = areasWhereIam.size() - 1; 0 <= i; --i) {
- areasWhereIam.write[i] = nullptr;
+ for (uint32_t i = 0; i < areasWhereIam.size(); i += 1) {
+ areasWhereIam[i] = nullptr;
}
btBody->setSleepingThresholds(0.2, 0.2);
@@ -307,7 +305,7 @@ void RigidBodyBullet::main_shape_changed() {
set_continuous_collision_detection(is_continuous_collision_detection_enabled()); // Reset
}
-void RigidBodyBullet::reload_body() {
+void RigidBodyBullet::do_reload_body() {
if (space) {
space->remove_rigid_body(this);
if (get_main_shape()) {
@@ -320,28 +318,30 @@ void RigidBodyBullet::set_space(SpaceBullet *p_space) {
// Clear the old space if there is one
if (space) {
can_integrate_forces = false;
+ isScratchedSpaceOverrideModificator = false;
// Remove all eventual constraints
assert_no_constraints();
// Remove this object form the physics world
+ space->unregister_collision_object(this);
space->remove_rigid_body(this);
}
space = p_space;
if (space) {
- space->add_rigid_body(this);
+ space->register_collision_object(this);
+ reload_body();
+ space->add_to_flush_queue(this);
}
}
void RigidBodyBullet::dispatch_callbacks() {
+ RigidCollisionObjectBullet::dispatch_callbacks();
+
/// The check isFirstTransformChanged is necessary in order to call integrated forces only when the first transform is sent
if ((btBody->isKinematicObject() || btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && can_integrate_forces) {
- if (omit_forces_integration) {
- btBody->clearForces();
- }
-
BulletPhysicsDirectBodyState3D *bodyDirect = BulletPhysicsDirectBodyState3D::get_singleton(this);
Variant variantBodyDirect = bodyDirect;
@@ -359,16 +359,22 @@ void RigidBodyBullet::dispatch_callbacks() {
}
}
+ previousActiveState = btBody->isActive();
+}
+
+void RigidBodyBullet::pre_process() {
+ RigidCollisionObjectBullet::pre_process();
+
if (isScratchedSpaceOverrideModificator || 0 < countGravityPointSpaces) {
isScratchedSpaceOverrideModificator = false;
reload_space_override_modificator();
}
- /// Lock axis
- btBody->setLinearVelocity(btBody->getLinearVelocity() * btBody->getLinearFactor());
- btBody->setAngularVelocity(btBody->getAngularVelocity() * btBody->getAngularFactor());
-
- previousActiveState = btBody->isActive();
+ if (is_active()) {
+ /// Lock axis
+ btBody->setLinearVelocity(btBody->getLinearVelocity() * btBody->getLinearFactor());
+ btBody->setAngularVelocity(btBody->getAngularVelocity() * btBody->getAngularFactor());
+ }
}
void RigidBodyBullet::set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata) {
@@ -389,7 +395,7 @@ void RigidBodyBullet::scratch_space_override_modificator() {
isScratchedSpaceOverrideModificator = true;
}
-void RigidBodyBullet::on_collision_filters_change() {
+void RigidBodyBullet::do_reload_collision_filters() {
if (space) {
space->reload_collision_filters(this);
}
@@ -402,14 +408,15 @@ void RigidBodyBullet::on_collision_checker_start() {
collisionsCount = 0;
// Swap array
- Vector<RigidBodyBullet *> *s = prev_collision_traces;
- prev_collision_traces = curr_collision_traces;
- curr_collision_traces = s;
+ SWAP(prev_collision_traces, curr_collision_traces);
}
void RigidBodyBullet::on_collision_checker_end() {
// Always true if active and not a static or kinematic body
isTransformChanged = btBody->isActive() && !btBody->isStaticOrKinematicObject();
+ if (isTransformChanged && space != nullptr) {
+ space->add_to_flush_queue(this);
+ }
}
bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index) {
@@ -417,7 +424,7 @@ bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const
return false;
}
- CollisionData &cd = collisions.write[collisionsCount];
+ CollisionData &cd = collisions[collisionsCount];
cd.hitLocalLocation = p_hitLocalLocation;
cd.otherObject = p_otherObject;
cd.hitWorldLocation = p_hitWorldLocation;
@@ -426,7 +433,7 @@ bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const
cd.other_object_shape = p_other_shape_index;
cd.local_shape = p_local_shape_index;
- curr_collision_traces->write[collisionsCount] = p_otherObject;
+ (*curr_collision_traces)[collisionsCount] = p_otherObject;
++collisionsCount;
return true;
@@ -445,11 +452,6 @@ void RigidBodyBullet::assert_no_constraints() {
if (btBody->getNumConstraintRefs()) {
WARN_PRINT("A body with a joints is destroyed. Please check the implementation in order to destroy the joint before the body.");
}
- /*for(int i = btBody->getNumConstraintRefs()-1; 0<=i; --i){
- btTypedConstraint* btConst = btBody->getConstraintRef(i);
- JointBullet* joint = static_cast<JointBullet*>( btConst->getUserConstraintPtr() );
- space->removeConstraint(joint);
- }*/
}
void RigidBodyBullet::set_activation_state(bool p_active) {
@@ -466,6 +468,7 @@ bool RigidBodyBullet::is_active() const {
void RigidBodyBullet::set_omit_forces_integration(bool p_omit) {
omit_forces_integration = p_omit;
+ scratch_space_override_modificator();
}
void RigidBodyBullet::set_param(PhysicsServer3D::BodyParameter p_param, real_t p_value) {
@@ -609,23 +612,23 @@ Variant RigidBodyBullet::get_state(PhysicsServer3D::BodyState p_state) const {
}
void RigidBodyBullet::apply_central_impulse(const Vector3 &p_impulse) {
- btVector3 btImpu;
- G_TO_B(p_impulse, btImpu);
+ btVector3 btImpulse;
+ G_TO_B(p_impulse, btImpulse);
if (Vector3() != p_impulse) {
btBody->activate();
}
- btBody->applyCentralImpulse(btImpu);
+ btBody->applyCentralImpulse(btImpulse);
}
-void RigidBodyBullet::apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse) {
- btVector3 btImpu;
- btVector3 btPos;
- G_TO_B(p_impulse, btImpu);
- G_TO_B(p_pos, btPos);
+void RigidBodyBullet::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) {
+ btVector3 btImpulse;
+ btVector3 btPosition;
+ G_TO_B(p_impulse, btImpulse);
+ G_TO_B(p_position, btPosition);
if (Vector3() != p_impulse) {
btBody->activate();
}
- btBody->applyImpulse(btImpu, btPos);
+ btBody->applyImpulse(btImpulse, btPosition);
}
void RigidBodyBullet::apply_torque_impulse(const Vector3 &p_impulse) {
@@ -637,15 +640,15 @@ void RigidBodyBullet::apply_torque_impulse(const Vector3 &p_impulse) {
btBody->applyTorqueImpulse(btImp);
}
-void RigidBodyBullet::apply_force(const Vector3 &p_force, const Vector3 &p_pos) {
+void RigidBodyBullet::apply_force(const Vector3 &p_force, const Vector3 &p_position) {
btVector3 btForce;
- btVector3 btPos;
+ btVector3 btPosition;
G_TO_B(p_force, btForce);
- G_TO_B(p_pos, btPos);
+ G_TO_B(p_position, btPosition);
if (Vector3() != p_force) {
btBody->activate();
}
- btBody->applyForce(btForce, btPos);
+ btBody->applyForce(btForce, btPosition);
}
void RigidBodyBullet::apply_central_force(const Vector3 &p_force) {
@@ -808,8 +811,8 @@ const btTransform &RigidBodyBullet::get_transform__bullet() const {
}
}
-void RigidBodyBullet::reload_shapes() {
- RigidCollisionObjectBullet::reload_shapes();
+void RigidBodyBullet::do_reload_shapes() {
+ RigidCollisionObjectBullet::do_reload_shapes();
const btScalar invMass = btBody->getInvMass();
const btScalar mass = invMass == 0 ? 0 : 1 / invMass;
@@ -841,15 +844,15 @@ void RigidBodyBullet::on_enter_area(AreaBullet *p_area) {
for (int i = 0; i < areaWhereIamCount; ++i) {
if (nullptr == areasWhereIam[i]) {
// This area has the highest priority
- areasWhereIam.write[i] = p_area;
+ areasWhereIam[i] = p_area;
break;
} else {
if (areasWhereIam[i]->get_spOv_priority() > p_area->get_spOv_priority()) {
// The position was found, just shift all elements
- for (int j = i; j < areaWhereIamCount; ++j) {
- areasWhereIam.write[j + 1] = areasWhereIam[j];
+ for (int j = areaWhereIamCount; j > i; j--) {
+ areasWhereIam[j] = areasWhereIam[j - 1];
}
- areasWhereIam.write[i] = p_area;
+ areasWhereIam[i] = p_area;
break;
}
}
@@ -873,7 +876,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
if (p_area == areasWhereIam[i]) {
// The area was found, just shift down all elements
for (int j = i; j < areaWhereIamCount; ++j) {
- areasWhereIam.write[j] = areasWhereIam[j + 1];
+ areasWhereIam[j] = areasWhereIam[j + 1];
}
wasTheAreaFound = true;
break;
@@ -886,7 +889,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
}
--areaWhereIamCount;
- areasWhereIam.write[areaWhereIamCount] = nullptr; // Even if this is not required, I clear the last element to be safe
+ areasWhereIam[areaWhereIamCount] = nullptr; // Even if this is not required, I clear the last element to be safe
if (PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED != p_area->get_spOv_mode()) {
scratch_space_override_modificator();
}
@@ -894,41 +897,35 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
}
void RigidBodyBullet::reload_space_override_modificator() {
- // Make sure that kinematic bodies have their total gravity calculated
- if (!is_active() && PhysicsServer3D::BODY_MODE_KINEMATIC != mode) {
+ if (mode == PhysicsServer3D::BODY_MODE_STATIC) {
return;
}
- Vector3 newGravity(0.0, 0.0, 0.0);
+ Vector3 newGravity;
real_t newLinearDamp = MAX(0.0, linearDamp);
real_t newAngularDamp = MAX(0.0, angularDamp);
- AreaBullet *currentArea;
- // Variable used to calculate new gravity for gravity point areas, it is pointed by currentGravity pointer
- Vector3 support_gravity(0, 0, 0);
-
bool stopped = false;
- for (int i = areaWhereIamCount - 1; (0 <= i) && !stopped; --i) {
- currentArea = areasWhereIam[i];
+ for (int i = 0; i < areaWhereIamCount && !stopped; i += 1) {
+ AreaBullet *currentArea = areasWhereIam[i];
if (!currentArea || PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED == currentArea->get_spOv_mode()) {
continue;
}
+ Vector3 support_gravity;
+
/// Here is calculated the gravity
if (currentArea->is_spOv_gravityPoint()) {
/// It calculates the direction of new gravity
support_gravity = currentArea->get_transform().xform(currentArea->get_spOv_gravityVec()) - get_transform().get_origin();
- real_t distanceMag = support_gravity.length();
+
+ const real_t distanceMag = support_gravity.length();
// Normalized in this way to avoid the double call of function "length()"
if (distanceMag == 0) {
- support_gravity.x = 0;
- support_gravity.y = 0;
- support_gravity.z = 0;
+ support_gravity = Vector3();
} else {
- support_gravity.x /= distanceMag;
- support_gravity.y /= distanceMag;
- support_gravity.z /= distanceMag;
+ support_gravity /= distanceMag;
}
/// Here is calculated the final gravity
@@ -990,10 +987,17 @@ void RigidBodyBullet::reload_space_override_modificator() {
newAngularDamp += space->get_angular_damp();
}
- btVector3 newBtGravity;
- G_TO_B(newGravity * gravity_scale, newBtGravity);
+ total_gravity = newGravity;
+
+ if (omit_forces_integration) {
+ // Custom behaviour.
+ btBody->setGravity(btVector3(0, 0, 0));
+ } else {
+ btVector3 newBtGravity;
+ G_TO_B(newGravity * gravity_scale, newBtGravity);
+ btBody->setGravity(newBtGravity);
+ }
- btBody->setGravity(newBtGravity);
btBody->setDamping(newLinearDamp, newAngularDamp);
}
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index 6d159504b8..047645677b 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -88,57 +88,57 @@ private:
BulletPhysicsDirectBodyState3D() {}
public:
- virtual Vector3 get_total_gravity() const;
- virtual float get_total_angular_damp() const;
- virtual float get_total_linear_damp() const;
+ virtual Vector3 get_total_gravity() const override;
+ virtual float get_total_angular_damp() const override;
+ virtual float get_total_linear_damp() const override;
- virtual Vector3 get_center_of_mass() const;
- virtual Basis get_principal_inertia_axes() const;
+ virtual Vector3 get_center_of_mass() const override;
+ virtual Basis get_principal_inertia_axes() const override;
// get the mass
- virtual float get_inverse_mass() const;
+ virtual float get_inverse_mass() const override;
// get density of this body space
- virtual Vector3 get_inverse_inertia() const;
+ virtual Vector3 get_inverse_inertia() const override;
// get density of this body space
- virtual Basis get_inverse_inertia_tensor() const;
+ virtual Basis get_inverse_inertia_tensor() const override;
- virtual void set_linear_velocity(const Vector3 &p_velocity);
- virtual Vector3 get_linear_velocity() const;
+ virtual void set_linear_velocity(const Vector3 &p_velocity) override;
+ virtual Vector3 get_linear_velocity() const override;
- virtual void set_angular_velocity(const Vector3 &p_velocity);
- virtual Vector3 get_angular_velocity() const;
+ virtual void set_angular_velocity(const Vector3 &p_velocity) override;
+ virtual Vector3 get_angular_velocity() const override;
- virtual void set_transform(const Transform &p_transform);
- virtual Transform get_transform() const;
+ virtual void set_transform(const Transform &p_transform) override;
+ virtual Transform get_transform() const override;
- virtual void add_central_force(const Vector3 &p_force);
- virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos);
- virtual void add_torque(const Vector3 &p_torque);
- virtual void apply_central_impulse(const Vector3 &p_impulse);
- virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse);
- virtual void apply_torque_impulse(const Vector3 &p_impulse);
+ virtual void add_central_force(const Vector3 &p_force) override;
+ virtual void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override;
+ virtual void add_torque(const Vector3 &p_torque) override;
+ virtual void apply_central_impulse(const Vector3 &p_impulse) override;
+ virtual void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override;
+ virtual void apply_torque_impulse(const Vector3 &p_impulse) override;
- virtual void set_sleep_state(bool p_sleep);
- virtual bool is_sleeping() const;
+ virtual void set_sleep_state(bool p_sleep) override;
+ virtual bool is_sleeping() const override;
- virtual int get_contact_count() const;
+ virtual int get_contact_count() const override;
- virtual Vector3 get_contact_local_position(int p_contact_idx) const;
- virtual Vector3 get_contact_local_normal(int p_contact_idx) const;
- virtual float get_contact_impulse(int p_contact_idx) const;
- virtual int get_contact_local_shape(int p_contact_idx) const;
+ virtual Vector3 get_contact_local_position(int p_contact_idx) const override;
+ virtual Vector3 get_contact_local_normal(int p_contact_idx) const override;
+ virtual float get_contact_impulse(int p_contact_idx) const override;
+ virtual int get_contact_local_shape(int p_contact_idx) const override;
- virtual RID get_contact_collider(int p_contact_idx) const;
- virtual Vector3 get_contact_collider_position(int p_contact_idx) const;
- virtual ObjectID get_contact_collider_id(int p_contact_idx) const;
- virtual int get_contact_collider_shape(int p_contact_idx) const;
- virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const;
+ virtual RID get_contact_collider(int p_contact_idx) const override;
+ virtual Vector3 get_contact_collider_position(int p_contact_idx) const override;
+ virtual ObjectID get_contact_collider_id(int p_contact_idx) const override;
+ virtual int get_contact_collider_shape(int p_contact_idx) const override;
+ virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const override;
- virtual real_t get_step() const { return deltaTime; }
- virtual void integrate_forces() {
+ virtual real_t get_step() const override { return deltaTime; }
+ virtual void integrate_forces() override {
// Skip the execution of this function
}
- virtual PhysicsDirectSpaceState3D *get_space_state();
+ virtual PhysicsDirectSpaceState3D *get_space_state() override;
};
class RigidBodyBullet : public RigidCollisionObjectBullet {
@@ -171,7 +171,7 @@ public:
struct KinematicUtilities {
RigidBodyBullet *owner;
btScalar safe_margin;
- Vector<KinematicShape> shapes;
+ LocalVector<KinematicShape> shapes;
KinematicUtilities(RigidBodyBullet *p_owner);
~KinematicUtilities();
@@ -193,6 +193,7 @@ private:
PhysicsServer3D::BodyMode mode;
GodotMotionState *godotMotionState;
btRigidBody *btBody;
+ Vector3 total_gravity;
uint16_t locked_axis = 0;
real_t mass = 1;
real_t gravity_scale = 1;
@@ -202,18 +203,18 @@ private:
bool omit_forces_integration = false;
bool can_integrate_forces = false;
- Vector<CollisionData> collisions;
- Vector<RigidBodyBullet *> collision_traces_1;
- Vector<RigidBodyBullet *> collision_traces_2;
- Vector<RigidBodyBullet *> *prev_collision_traces;
- Vector<RigidBodyBullet *> *curr_collision_traces;
+ LocalVector<CollisionData> collisions;
+ LocalVector<RigidBodyBullet *> collision_traces_1;
+ LocalVector<RigidBodyBullet *> collision_traces_2;
+ LocalVector<RigidBodyBullet *> *prev_collision_traces;
+ LocalVector<RigidBodyBullet *> *curr_collision_traces;
// these parameters are used to avoid vector resize
- int maxCollisionsDetection = 0;
- int collisionsCount = 0;
- int prev_collision_count = 0;
+ uint32_t maxCollisionsDetection = 0;
+ uint32_t collisionsCount = 0;
+ uint32_t prev_collision_count = 0;
- Vector<AreaBullet *> areasWhereIam;
+ LocalVector<AreaBullet *> areasWhereIam;
// these parameters are used to avoid vector resize
int maxAreasWhereIam = 10;
int areaWhereIamCount = 0;
@@ -235,21 +236,20 @@ public:
_FORCE_INLINE_ btRigidBody *get_bt_rigid_body() { return btBody; }
- virtual void main_shape_changed();
- virtual void reload_body();
- virtual void set_space(SpaceBullet *p_space);
+ virtual void main_shape_changed() override;
+ virtual void do_reload_body() override;
+ virtual void set_space(SpaceBullet *p_space) override;
- virtual void dispatch_callbacks();
+ virtual void dispatch_callbacks() override;
+ virtual void pre_process() override;
void set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch_space_override_modificator();
- virtual void on_collision_filters_change();
- virtual void on_collision_checker_start();
- virtual void on_collision_checker_end();
-
- void set_max_collisions_detection(int p_maxCollisionsDetection) {
- ERR_FAIL_COND(0 > p_maxCollisionsDetection);
+ virtual void do_reload_collision_filters() override;
+ virtual void on_collision_checker_start() override;
+ virtual void on_collision_checker_end() override;
+ void set_max_collisions_detection(uint32_t p_maxCollisionsDetection) {
maxCollisionsDetection = p_maxCollisionsDetection;
collisions.resize(p_maxCollisionsDetection);
@@ -284,12 +284,12 @@ public:
void set_state(PhysicsServer3D::BodyState p_state, const Variant &p_variant);
Variant get_state(PhysicsServer3D::BodyState p_state) const;
- void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse);
void apply_central_impulse(const Vector3 &p_impulse);
+ void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3());
void apply_torque_impulse(const Vector3 &p_impulse);
- void apply_force(const Vector3 &p_force, const Vector3 &p_pos);
void apply_central_force(const Vector3 &p_force);
+ void apply_force(const Vector3 &p_force, const Vector3 &p_position = Vector3());
void apply_torque(const Vector3 &p_torque);
void set_applied_force(const Vector3 &p_force);
@@ -312,19 +312,19 @@ public:
void set_angular_velocity(const Vector3 &p_velocity);
Vector3 get_angular_velocity() const;
- virtual void set_transform__bullet(const btTransform &p_global_transform);
- virtual const btTransform &get_transform__bullet() const;
+ virtual void set_transform__bullet(const btTransform &p_global_transform) override;
+ virtual const btTransform &get_transform__bullet() const override;
- virtual void reload_shapes();
+ virtual void do_reload_shapes() override;
- virtual void on_enter_area(AreaBullet *p_area);
- virtual void on_exit_area(AreaBullet *p_area);
+ virtual void on_enter_area(AreaBullet *p_area) override;
+ virtual void on_exit_area(AreaBullet *p_area) override;
void reload_space_override_modificator();
/// Kinematic
void reload_kinematic_shapes();
- virtual void notify_transform_changed();
+ virtual void notify_transform_changed() override;
private:
void _internal_set_mass(real_t p_mass);
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index d53f1e7d17..74d6e073b3 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -46,9 +46,15 @@
@author AndreaCatania
*/
-ShapeBullet::ShapeBullet() {}
+ShapeBullet::ShapeBullet() {
+}
-ShapeBullet::~ShapeBullet() {}
+ShapeBullet::~ShapeBullet() {
+ if (default_shape != nullptr) {
+ bulletdelete(default_shape);
+ default_shape = nullptr;
+ }
+}
btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 s;
@@ -56,6 +62,22 @@ btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale,
return create_bt_shape(s, p_extra_edge);
}
+btCollisionShape *ShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ if (p_extra_edge == 0.0 && (p_implicit_scale - btVector3(1, 1, 1)).length2() <= CMP_EPSILON) {
+ return default_shape;
+ }
+
+ return internal_create_bt_shape(p_implicit_scale, p_extra_edge);
+}
+
+void ShapeBullet::destroy_bt_shape(btCollisionShape *p_shape) const {
+ if (p_shape != default_shape && p_shape != old_default_shape) {
+ if (likely(p_shape != nullptr)) {
+ bulletdelete(p_shape);
+ }
+ }
+}
+
btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
p_btShape->setUserPointer(const_cast<ShapeBullet *>(this));
p_btShape->setMargin(margin);
@@ -63,10 +85,21 @@ btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
}
void ShapeBullet::notifyShapeChanged() {
+ // Store the old shape ptr so to not lose the reference pointer.
+ old_default_shape = default_shape;
+ // Create the new default shape with the new data.
+ default_shape = internal_create_bt_shape(btVector3(1, 1, 1));
+
for (Map<ShapeOwnerBullet *, int>::Element *E = owners.front(); E; E = E->next()) {
ShapeOwnerBullet *owner = static_cast<ShapeOwnerBullet *>(E->key());
owner->shape_changed(owner->find_shape(this));
}
+
+ if (old_default_shape) {
+ // At this point now one has the old default shape; just delete it.
+ bulletdelete(old_default_shape);
+ old_default_shape = nullptr;
+ }
}
void ShapeBullet::add_owner(ShapeOwnerBullet *p_owner) {
@@ -186,7 +219,7 @@ void PlaneShapeBullet::setup(const Plane &p_plane) {
notifyShapeChanged();
}
-btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *PlaneShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 btPlaneNormal;
G_TO_B(plane.normal, btPlaneNormal);
return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d));
@@ -214,7 +247,7 @@ void SphereShapeBullet::setup(real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *SphereShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *SphereShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
return prepare(ShapeBullet::create_shape_sphere(radius * p_implicit_scale[0] + p_extra_edge));
}
@@ -241,7 +274,7 @@ void BoxShapeBullet::setup(const Vector3 &p_half_extents) {
notifyShapeChanged();
}
-btCollisionShape *BoxShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *BoxShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
return prepare(ShapeBullet::create_shape_box((half_extents * p_implicit_scale) + btVector3(p_extra_edge, p_extra_edge, p_extra_edge)));
}
@@ -274,8 +307,8 @@ void CapsuleShapeBullet::setup(real_t p_height, real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
- return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1] + p_extra_edge));
+btCollisionShape *CapsuleShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1]));
}
/* Cylinder */
@@ -307,7 +340,7 @@ void CylinderShapeBullet::setup(real_t p_height, real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *CylinderShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *CylinderShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
return prepare(ShapeBullet::create_shape_cylinder(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
}
@@ -349,7 +382,7 @@ void ConvexPolygonShapeBullet::setup(const Vector<Vector3> &p_vertices) {
notifyShapeChanged();
}
-btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *ConvexPolygonShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
if (!vertices.size()) {
// This is necessary since 0 vertices
return prepare(ShapeBullet::create_shape_empty());
@@ -431,7 +464,7 @@ void ConcavePolygonShapeBullet::setup(Vector<Vector3> p_faces) {
notifyShapeChanged();
}
-btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *ConcavePolygonShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs = ShapeBullet::create_shape_concave(meshShape);
if (!cs) {
// This is necessary since if 0 faces the creation of concave return null
@@ -471,6 +504,9 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
int l_width = d["width"];
int l_depth = d["depth"];
+ ERR_FAIL_COND_MSG(l_width < 2, "Map width must be at least 2.");
+ ERR_FAIL_COND_MSG(l_depth < 2, "Map depth must be at least 2.");
+
// TODO This code will need adjustments if real_t is set to `double`,
// because that precision is unnecessary for a heightmap and Bullet doesn't support it...
@@ -555,7 +591,7 @@ void HeightMapShapeBullet::setup(Vector<real_t> &p_heights, int p_width, int p_d
notifyShapeChanged();
}
-btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *HeightMapShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
@@ -588,6 +624,6 @@ void RayShapeBullet::setup(real_t p_length, bool p_slips_on_slope) {
notifyShapeChanged();
}
-btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *RayShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_extra_edge, slips_on_slope));
}
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index b24ded574f..6ca4d36a23 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -31,7 +31,7 @@
#ifndef SHAPE_BULLET_H
#define SHAPE_BULLET_H
-#include "core/math/geometry.h"
+#include "core/math/geometry_3d.h"
#include "core/variant.h"
#include "rid_bullet.h"
#include "servers/physics_server_3d.h"
@@ -53,6 +53,10 @@ class ShapeBullet : public RIDBullet {
Map<ShapeOwnerBullet *, int> owners;
real_t margin = 0.04;
+ // Contains the default shape.
+ btCollisionShape *default_shape = nullptr;
+ btCollisionShape *old_default_shape = nullptr;
+
protected:
/// return self
btCollisionShape *prepare(btCollisionShape *p_btShape) const;
@@ -63,7 +67,11 @@ public:
virtual ~ShapeBullet();
btCollisionShape *create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge = 0);
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0;
+ btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+
+ void destroy_bt_shape(btCollisionShape *p_shape) const;
+
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0;
void add_owner(ShapeOwnerBullet *p_owner);
void remove_owner(ShapeOwnerBullet *p_owner, bool p_permanentlyFromThisBody = false);
@@ -102,7 +110,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Plane &p_plane);
@@ -118,7 +126,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_radius);
@@ -134,7 +142,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector3 &p_half_extents);
@@ -152,7 +160,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_height, real_t p_radius);
@@ -170,7 +178,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(real_t p_height, real_t p_radius);
@@ -186,7 +194,7 @@ public:
void get_vertices(Vector<Vector3> &out_vertices);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector<Vector3> &p_vertices);
@@ -204,7 +212,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(Vector<Vector3> p_faces);
@@ -223,7 +231,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
@@ -239,7 +247,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_length, bool p_slips_on_slope);
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index 6794d6c313..ee48b3c5f0 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -41,7 +41,7 @@ SoftBodyBullet::SoftBodyBullet() :
SoftBodyBullet::~SoftBodyBullet() {
}
-void SoftBodyBullet::reload_body() {
+void SoftBodyBullet::do_reload_body() {
if (space) {
space->remove_soft_body(this);
space->add_soft_body(this);
@@ -51,13 +51,15 @@ void SoftBodyBullet::reload_body() {
void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
isScratched = false;
+ space->unregister_collision_object(this);
space->remove_soft_body(this);
}
space = p_space;
if (space) {
- space->add_soft_body(this);
+ space->register_collision_object(this);
+ reload_body();
}
}
@@ -344,14 +346,14 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
indices_table.push_back(Vector<int>());
}
- indices_table.write[vertex_id].push_back(vs_vertex_index);
+ indices_table[vertex_id].push_back(vs_vertex_index);
vs_indices_to_physics_table.push_back(vertex_id);
}
}
const int indices_map_size(indices_table.size());
- Vector<btScalar> bt_vertices;
+ LocalVector<btScalar> bt_vertices;
{ // Parse vertices to bullet
@@ -359,13 +361,13 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
const Vector3 *p_vertices_read = p_vertices.ptr();
for (int i = 0; i < indices_map_size; ++i) {
- bt_vertices.write[3 * i + 0] = p_vertices_read[indices_table[i][0]].x;
- bt_vertices.write[3 * i + 1] = p_vertices_read[indices_table[i][0]].y;
- bt_vertices.write[3 * i + 2] = p_vertices_read[indices_table[i][0]].z;
+ bt_vertices[3 * i + 0] = p_vertices_read[indices_table[i][0]].x;
+ bt_vertices[3 * i + 1] = p_vertices_read[indices_table[i][0]].y;
+ bt_vertices[3 * i + 2] = p_vertices_read[indices_table[i][0]].z;
}
}
- Vector<int> bt_triangles;
+ LocalVector<int> bt_triangles;
const int triangles_size(p_indices.size() / 3);
{ // Parse indices
@@ -375,9 +377,9 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
const int *p_indices_read = p_indices.ptr();
for (int i = 0; i < triangles_size; ++i) {
- bt_triangles.write[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]];
- bt_triangles.write[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]];
- bt_triangles.write[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]];
+ bt_triangles[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]];
+ bt_triangles[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]];
+ bt_triangles[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]];
}
}
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
index da8a2412ed..229204b539 100644
--- a/modules/bullet/soft_body_bullet.h
+++ b/modules/bullet/soft_body_bullet.h
@@ -32,7 +32,6 @@
#define SOFT_BODY_BULLET_H
#include "collision_object_bullet.h"
-#include "scene/resources/material.h" // TODO remove this please
#ifdef None
/// This is required to remove the macro None defined by x11 compiler because this word "None" is used internally by Bullet
@@ -58,7 +57,7 @@
class SoftBodyBullet : public CollisionObjectBullet {
private:
btSoftBody *bt_soft_body = nullptr;
- Vector<Vector<int>> indices_table;
+ LocalVector<Vector<int>> indices_table;
btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody
bool isScratched = false;
@@ -73,7 +72,7 @@ private:
real_t pose_matching_coefficient = 0.; // [0,1]
real_t damping_coefficient = 0.01; // [0,1]
real_t drag_coefficient = 0.; // [0,1]
- Vector<int> pinned_nodes;
+ LocalVector<int> pinned_nodes;
// Other property to add
//btScalar kVC; // Volume conversation coefficient [0,+inf]
@@ -87,15 +86,14 @@ public:
SoftBodyBullet();
~SoftBodyBullet();
- virtual void reload_body();
- virtual void set_space(SpaceBullet *p_space);
+ virtual void do_reload_body() override;
+ virtual void set_space(SpaceBullet *p_space) override;
- virtual void dispatch_callbacks() {}
- virtual void on_collision_filters_change() {}
- virtual void on_collision_checker_start() {}
- virtual void on_collision_checker_end() {}
- virtual void on_enter_area(AreaBullet *p_area);
- virtual void on_exit_area(AreaBullet *p_area);
+ virtual void do_reload_collision_filters() override {}
+ virtual void on_collision_checker_start() override {}
+ virtual void on_collision_checker_end() override {}
+ virtual void on_enter_area(AreaBullet *p_area) override;
+ virtual void on_exit_area(AreaBullet *p_area) override;
_FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; }
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 99f58e7059..d0515e7c97 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -123,10 +123,11 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
}
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, 0);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
- bulletdelete(btShape);
+ shape->destroy_bt_shape(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return 0;
}
@@ -146,25 +147,28 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
btQuery.m_closestDistanceThreshold = 0;
space->dynamicsWorld->contactTest(&collision_object, btQuery);
- bulletdelete(btConvex);
+ shape->destroy_bt_shape(btShape);
return btQuery.m_count;
}
bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) {
+ r_closest_safe = 0.0f;
+ r_closest_unsafe = 0.0f;
+ btVector3 bt_motion;
+ G_TO_B(p_motion, bt_motion);
+
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
if (!btShape->isConvex()) {
- bulletdelete(btShape);
+ shape->destroy_bt_shape(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return false;
}
btConvexShape *bt_convex_shape = static_cast<btConvexShape *>(btShape);
- btVector3 bt_motion;
- G_TO_B(p_motion, bt_motion);
-
btTransform bt_xform_from;
G_TO_B(p_xform, bt_xform_from);
UNSCALE_BT_BASIS(bt_xform_from);
@@ -172,15 +176,17 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
btTransform bt_xform_to(bt_xform_from);
bt_xform_to.getOrigin() += bt_motion;
+ if ((bt_xform_to.getOrigin() - bt_xform_from.getOrigin()).fuzzyZero()) {
+ shape->destroy_bt_shape(btShape);
+ return false;
+ }
+
GodotClosestConvexResultCallback btResult(bt_xform_from.getOrigin(), bt_xform_to.getOrigin(), &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btResult.m_collisionFilterGroup = 0;
btResult.m_collisionFilterMask = p_collision_mask;
space->dynamicsWorld->convexSweepTest(bt_convex_shape, bt_xform_from, bt_xform_to, btResult, space->dynamicsWorld->getDispatchInfo().m_allowedCcdPenetration);
- r_closest_unsafe = 1.0;
- r_closest_safe = 1.0;
-
if (btResult.hasHit()) {
const btScalar l = bt_motion.length();
r_closest_unsafe = btResult.m_closestHitFraction;
@@ -196,9 +202,12 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
r_info->collider_id = collision_object->get_instance_id();
r_info->shape = btResult.m_shapeId;
}
+ } else {
+ r_closest_safe = 1.0f;
+ r_closest_unsafe = 1.0f;
}
- bulletdelete(bt_convex_shape);
+ shape->destroy_bt_shape(btShape);
return true; // Mean success
}
@@ -209,10 +218,11 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
}
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
- bulletdelete(btShape);
+ shape->destroy_bt_shape(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return false;
}
@@ -233,17 +243,18 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
space->dynamicsWorld->contactTest(&collision_object, btQuery);
r_result_count = btQuery.m_count;
- bulletdelete(btConvex);
+ shape->destroy_bt_shape(btShape);
return btQuery.m_count;
}
bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
- bulletdelete(btShape);
+ shape->destroy_bt_shape(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return false;
}
@@ -263,7 +274,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
btQuery.m_closestDistanceThreshold = 0;
space->dynamicsWorld->contactTest(&collision_object, btQuery);
- bulletdelete(btConvex);
+ shape->destroy_bt_shape(btShape);
if (btQuery.m_collided) {
if (btCollisionObject::CO_RIGID_BODY == btQuery.m_rest_info_collision_object->getInternalType()) {
@@ -276,7 +287,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
}
Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const {
- RigidCollisionObjectBullet *rigid_object = space->get_physics_server()->get_rigid_collisin_object(p_object);
+ RigidCollisionObjectBullet *rigid_object = space->get_physics_server()->get_rigid_collision_object(p_object);
ERR_FAIL_COND_V(!rigid_object, Vector3());
btVector3 out_closest_point(0, 0, 0);
@@ -338,14 +349,46 @@ SpaceBullet::~SpaceBullet() {
destroy_world();
}
+void SpaceBullet::add_to_pre_flush_queue(CollisionObjectBullet *p_co) {
+ if (p_co->is_in_flush_queue == false) {
+ p_co->is_in_flush_queue = true;
+ queue_pre_flush.push_back(p_co);
+ }
+}
+
+void SpaceBullet::add_to_flush_queue(CollisionObjectBullet *p_co) {
+ if (p_co->is_in_flush_queue == false) {
+ p_co->is_in_flush_queue = true;
+ queue_flush.push_back(p_co);
+ }
+}
+
+void SpaceBullet::remove_from_any_queue(CollisionObjectBullet *p_co) {
+ if (p_co->is_in_flush_queue) {
+ p_co->is_in_flush_queue = false;
+ queue_pre_flush.erase(p_co);
+ queue_flush.erase(p_co);
+ }
+}
+
void SpaceBullet::flush_queries() {
- const btCollisionObjectArray &colObjArray = dynamicsWorld->getCollisionObjectArray();
- for (int i = colObjArray.size() - 1; 0 <= i; --i) {
- static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer())->dispatch_callbacks();
+ for (uint32_t i = 0; i < queue_pre_flush.size(); i += 1) {
+ queue_pre_flush[i]->dispatch_callbacks();
+ queue_pre_flush[i]->is_in_flush_queue = false;
}
+ for (uint32_t i = 0; i < queue_flush.size(); i += 1) {
+ queue_flush[i]->dispatch_callbacks();
+ queue_flush[i]->is_in_flush_queue = false;
+ }
+ queue_pre_flush.clear();
+ queue_flush.clear();
}
void SpaceBullet::step(real_t p_delta_time) {
+ for (uint32_t i = 0; i < collision_objects.size(); i += 1) {
+ collision_objects[i]->pre_process();
+ }
+
delta_time = p_delta_time;
dynamicsWorld->stepSimulation(p_delta_time, 0, 0);
}
@@ -438,16 +481,30 @@ real_t SpaceBullet::get_param(PhysicsServer3D::SpaceParameter p_param) {
}
void SpaceBullet::add_area(AreaBullet *p_area) {
+#ifdef TOOLS_ENABLED
+ // This never happen, and there is no way for the user to trigger it.
+ // If in future a bug is introduced into this bullet integration and this
+ // function is called twice, the crash will notify the developer that will
+ // fix it even before do the eventual PR.
+ CRASH_COND(p_area->is_in_world);
+#endif
areas.push_back(p_area);
dynamicsWorld->addCollisionObject(p_area->get_bt_ghost(), p_area->get_collision_layer(), p_area->get_collision_mask());
+ p_area->is_in_world = true;
}
void SpaceBullet::remove_area(AreaBullet *p_area) {
- areas.erase(p_area);
- dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost());
+ if (p_area->is_in_world) {
+ areas.erase(p_area);
+ dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost());
+ p_area->is_in_world = false;
+ }
}
void SpaceBullet::reload_collision_filters(AreaBullet *p_area) {
+ if (p_area->is_in_world == false) {
+ return;
+ }
btGhostObject *ghost_object = p_area->get_bt_ghost();
btBroadphaseProxy *ghost_proxy = ghost_object->getBroadphaseHandle();
@@ -457,24 +514,47 @@ void SpaceBullet::reload_collision_filters(AreaBullet *p_area) {
dynamicsWorld->refreshBroadphaseProxy(ghost_object);
}
+void SpaceBullet::register_collision_object(CollisionObjectBullet *p_object) {
+ collision_objects.push_back(p_object);
+}
+
+void SpaceBullet::unregister_collision_object(CollisionObjectBullet *p_object) {
+ remove_from_any_queue(p_object);
+ collision_objects.erase(p_object);
+}
+
void SpaceBullet::add_rigid_body(RigidBodyBullet *p_body) {
+#ifdef TOOLS_ENABLED
+ // This never happen, and there is no way for the user to trigger it.
+ // If in future a bug is introduced into this bullet integration and this
+ // function is called twice, the crash will notify the developer that will
+ // fix it even before do the eventual PR.
+ CRASH_COND(p_body->is_in_world);
+#endif
if (p_body->is_static()) {
dynamicsWorld->addCollisionObject(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
} else {
dynamicsWorld->addRigidBody(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
p_body->scratch_space_override_modificator();
}
+ p_body->is_in_world = true;
}
void SpaceBullet::remove_rigid_body(RigidBodyBullet *p_body) {
- if (p_body->is_static()) {
- dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body());
- } else {
- dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body());
+ if (p_body->is_in_world) {
+ if (p_body->is_static()) {
+ dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body());
+ } else {
+ dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body());
+ }
+ p_body->is_in_world = false;
}
}
void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) {
+ if (p_body->is_in_world == false) {
+ return;
+ }
btRigidBody *rigid_body = p_body->get_bt_rigid_body();
btBroadphaseProxy *body_proxy = rigid_body->getBroadphaseProxy();
@@ -531,10 +611,6 @@ void SpaceBullet::remove_all_collision_objects() {
}
}
-void onBulletPreTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep) {
- static_cast<SpaceBullet *>(p_dynamicsWorld->getWorldUserInfo())->flush_queries();
-}
-
void onBulletTickCallback(btDynamicsWorld *p_dynamicsWorld, btScalar timeStep) {
const btCollisionObjectArray &colObjArray = p_dynamicsWorld->getCollisionObjectArray();
@@ -602,7 +678,6 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) {
dynamicsWorld->setWorldUserInfo(this);
- dynamicsWorld->setInternalTickCallback(onBulletPreTickCallback, this, true);
dynamicsWorld->setInternalTickCallback(onBulletTickCallback, this, false);
dynamicsWorld->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(ghostPairCallback); // Setup ghost check
dynamicsWorld->getPairCache()->setOverlapFilterCallback(godotFilterCallback);
@@ -659,7 +734,7 @@ void SpaceBullet::check_ghost_overlaps() {
/// 1. Reset all states
for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) {
- AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects.write[i];
+ AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects[i];
// This check prevent the overwrite of ENTER state
// if this function is called more times before dispatchCallbacks
if (otherObj.state != AreaBullet::OVERLAP_STATE_ENTER) {
@@ -939,7 +1014,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
motionVec->end();
#endif
- for (int shIndex = 0; shIndex < shape_count && !motion.fuzzyZero(); ++shIndex) {
+ for (int shIndex = 0; shIndex < shape_count; ++shIndex) {
if (p_body->is_shape_disabled(shIndex)) {
continue;
}
@@ -961,6 +1036,11 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
btTransform shape_world_to(shape_world_from);
shape_world_to.getOrigin() += motion;
+ if ((shape_world_to.getOrigin() - shape_world_from.getOrigin()).fuzzyZero()) {
+ motion = btVector3(0, 0, 0);
+ break;
+ }
+
GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia);
btResult.m_collisionFilterGroup = p_body->get_collision_layer();
btResult.m_collisionFilterMask = p_body->get_collision_mask();
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index 5ff421ef52..897f902fe1 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -31,8 +31,8 @@
#ifndef SPACE_BULLET_H
#define SPACE_BULLET_H
+#include "core/local_vector.h"
#include "core/variant.h"
-#include "core/vector.h"
#include "godot_result_callbacks.h"
#include "rid_bullet.h"
#include "servers/physics_server_3d.h"
@@ -76,14 +76,14 @@ private:
public:
BulletPhysicsDirectSpaceState(SpaceBullet *p_space);
- virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
- virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false);
- virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
- virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr);
+ virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) override;
+ virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) override;
/// Returns the list of contacts pairs in this order: Local contact, other body contact
- virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
- virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
- virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const;
+ virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const override;
};
class SpaceBullet : public RIDBullet {
@@ -110,23 +110,33 @@ class SpaceBullet : public RIDBullet {
real_t linear_damp = 0.0;
real_t angular_damp = 0.0;
- Vector<AreaBullet *> areas;
+ LocalVector<CollisionObjectBullet *> queue_pre_flush;
+ LocalVector<CollisionObjectBullet *> queue_flush;
+ LocalVector<CollisionObjectBullet *> collision_objects;
+ LocalVector<AreaBullet *> areas;
- Vector<Vector3> contactDebug;
- int contactDebugCount = 0;
+ LocalVector<Vector3> contactDebug;
+ uint32_t contactDebugCount = 0;
real_t delta_time = 0.;
public:
SpaceBullet();
virtual ~SpaceBullet();
+ void add_to_flush_queue(CollisionObjectBullet *p_co);
+ void add_to_pre_flush_queue(CollisionObjectBullet *p_co);
+ void remove_from_any_queue(CollisionObjectBullet *p_co);
+
void flush_queries();
real_t get_delta_time() { return delta_time; }
void step(real_t p_delta_time);
- _FORCE_INLINE_ btBroadphaseInterface *get_broadphase() { return broadphase; }
- _FORCE_INLINE_ btCollisionDispatcher *get_dispatcher() { return dispatcher; }
- _FORCE_INLINE_ btSoftBodyWorldInfo *get_soft_body_world_info() { return soft_body_world_info; }
+ _FORCE_INLINE_ btBroadphaseInterface *get_broadphase() const { return broadphase; }
+ _FORCE_INLINE_ btDefaultCollisionConfiguration *get_collision_configuration() const { return collisionConfiguration; }
+ _FORCE_INLINE_ btCollisionDispatcher *get_dispatcher() const { return dispatcher; }
+ _FORCE_INLINE_ btConstraintSolver *get_solver() const { return solver; }
+ _FORCE_INLINE_ btDiscreteDynamicsWorld *get_dynamic_world() const { return dynamicsWorld; }
+ _FORCE_INLINE_ btSoftBodyWorldInfo *get_soft_body_world_info() const { return soft_body_world_info; }
_FORCE_INLINE_ bool is_using_soft_world() { return soft_body_world_info; }
/// Used to set some parameters to Bullet world
@@ -147,6 +157,9 @@ public:
void remove_area(AreaBullet *p_area);
void reload_collision_filters(AreaBullet *p_area);
+ void register_collision_object(CollisionObjectBullet *p_object);
+ void unregister_collision_object(CollisionObjectBullet *p_object);
+
void add_rigid_body(RigidBodyBullet *p_body);
void remove_rigid_body(RigidBodyBullet *p_body);
void reload_collision_filters(RigidBodyBullet *p_body);
@@ -170,7 +183,7 @@ public:
}
_FORCE_INLINE_ void add_debug_contact(const Vector3 &p_contact) {
if (contactDebugCount < contactDebug.size()) {
- contactDebug.write[contactDebugCount++] = p_contact;
+ contactDebug[contactDebugCount++] = p_contact;
}
}
_FORCE_INLINE_ Vector<Vector3> get_debug_contacts() { return contactDebug; }
diff --git a/modules/camera/SCsub b/modules/camera/SCsub
index 63c4e9fbab..631a65bde2 100644
--- a/modules/camera/SCsub
+++ b/modules/camera/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_camera = env_modules.Clone()
if env["platform"] == "iphone":
+ # (iOS) Enable module support
+ env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
# (iOS) Build as separate static library
modules_sources = []
env_camera.add_source_files(modules_sources, "register_types.cpp")
diff --git a/modules/camera/camera_ios.mm b/modules/camera/camera_ios.mm
index f01135f251..c10b13b2af 100644
--- a/modules/camera/camera_ios.mm
+++ b/modules/camera/camera_ios.mm
@@ -158,25 +158,31 @@
} else if (dataCbCr == NULL) {
print_line("Couldn't access CbCr pixel buffer data");
} else {
- UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
+ UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
+
+ if (@available(iOS 13, *)) {
+ orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ orientation = [[UIApplication sharedApplication] statusBarOrientation];
+#endif
+ }
+
Ref<Image> img[2];
{
// do Y
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
- int _bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
if ((width[0] != new_width) || (height[0] != new_height)) {
- // printf("Camera Y plane %i, %i - %i\n", new_width, new_height, bytes_per_row);
-
width[0] = new_width;
height[0] = new_height;
img_data[0].resize(new_width * new_height);
}
uint8_t *w = img_data[0].ptrw();
- memcpy(w.ptr(), dataY, new_width * new_height);
+ memcpy(w, dataY, new_width * new_height);
img[0].instance();
img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
@@ -184,20 +190,17 @@
{
// do CbCr
- int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
- int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
- int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
+ size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
+ size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
if ((width[1] != new_width) || (height[1] != new_height)) {
- // printf("Camera CbCr plane %i, %i - %i\n", new_width, new_height, bytes_per_row);
-
width[1] = new_width;
height[1] = new_height;
img_data[1].resize(2 * new_width * new_height);
}
uint8_t *w = img_data[1].ptrw();
- memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height);
+ memcpy(w, dataCbCr, 2 * new_width * new_height);
///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
img[1].instance();
@@ -359,41 +362,59 @@ void CameraIOS::update_feeds() {
// this way of doing things is deprecated but still works,
// rewrite to using AVCaptureDeviceDiscoverySession
- AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeBuiltInTelephotoCamera, AVCaptureDeviceTypeBuiltInDualCamera, AVCaptureDeviceTypeBuiltInTrueDepthCamera, AVCaptureDeviceTypeBuiltInWideAngleCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
+ NSMutableArray *deviceTypes = [NSMutableArray array];
- // remove devices that are gone..
- for (int i = feeds.size() - 1; i >= 0; i--) {
- Ref<CameraFeedIOS> feed(feeds[i]);
+ if (@available(iOS 10, *)) {
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInWideAngleCamera];
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTelephotoCamera];
- if (feed.is_null()) {
- // feed not managed by us
- } else if (![session.devices containsObject:feed->get_device()]) {
- // remove it from our array, this will also destroy it ;)
- remove_feed(feed);
- };
- };
+ if (@available(iOS 10.2, *)) {
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualCamera];
+ }
- // add new devices..
- for (AVCaptureDevice *device in session.devices) {
- bool found = false;
+ if (@available(iOS 11.1, *)) {
+ [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTrueDepthCamera];
+ }
+
+ AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
+ discoverySessionWithDeviceTypes:deviceTypes
+ mediaType:AVMediaTypeVideo
+ position:AVCaptureDevicePositionUnspecified];
- for (int i = 0; i < feeds.size() && !found; i++) {
+ // remove devices that are gone..
+ for (int i = feeds.size() - 1; i >= 0; i--) {
Ref<CameraFeedIOS> feed(feeds[i]);
if (feed.is_null()) {
// feed not managed by us
- } else if (feed->get_device() == device) {
- found = true;
+ } else if (![session.devices containsObject:feed->get_device()]) {
+ // remove it from our array, this will also destroy it ;)
+ remove_feed(feed);
};
};
- if (!found) {
- Ref<CameraFeedIOS> newfeed;
- newfeed.instance();
- newfeed->set_device(device);
- add_feed(newfeed);
+ // add new devices..
+ for (AVCaptureDevice *device in session.devices) {
+ bool found = false;
+
+ for (int i = 0; i < feeds.size() && !found; i++) {
+ Ref<CameraFeedIOS> feed(feeds[i]);
+
+ if (feed.is_null()) {
+ // feed not managed by us
+ } else if (feed->get_device() == device) {
+ found = true;
+ };
+ };
+
+ if (!found) {
+ Ref<CameraFeedIOS> newfeed;
+ newfeed.instance();
+ newfeed->set_device(device);
+ add_feed(newfeed);
+ };
};
- };
+ }
};
CameraIOS::CameraIOS() {
diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_osx.mm
index 9a72174723..306632a016 100644
--- a/modules/camera/camera_osx.mm
+++ b/modules/camera/camera_osx.mm
@@ -313,7 +313,12 @@ MyDeviceNotifications *device_notifications = nil;
// CameraOSX - Subclass for our camera server on OSX
void CameraOSX::update_feeds() {
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101500
+ AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternalUnknown, AVCaptureDeviceTypeBuiltInWideAngleCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
+ NSArray *devices = session.devices;
+#else
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+#endif
// remove devices that are gone..
for (int i = feeds.size() - 1; i >= 0; i--) {
@@ -325,7 +330,6 @@ void CameraOSX::update_feeds() {
};
};
- // add new devices..
for (AVCaptureDevice *device in devices) {
bool found = false;
for (int i = 0; i < feeds.size() && !found; i++) {
diff --git a/modules/camera/camera_win.cpp b/modules/camera/camera_win.cpp
index 875f0b26bc..1646644be3 100644
--- a/modules/camera/camera_win.cpp
+++ b/modules/camera/camera_win.cpp
@@ -53,9 +53,9 @@ public:
void deactivate_feed();
};
-CameraFeedWindows::CameraFeedWindows(){
+CameraFeedWindows::CameraFeedWindows() {
///@TODO implement this, should store information about our available camera
-};
+}
CameraFeedWindows::~CameraFeedWindows() {
// make sure we stop recording if we are!
@@ -75,16 +75,16 @@ bool CameraFeedWindows::activate_feed() {
///@TODO we should probably have a callback method here that is being called by the
// camera API which provides frames and call back into the CameraServer to update our texture
-void CameraFeedWindows::deactivate_feed(){
+void CameraFeedWindows::deactivate_feed() {
///@TODO this should deactivate our camera and stop the process of capturing frames
-};
+}
//////////////////////////////////////////////////////////////////////////
// CameraWindows - Subclass for our camera server on windows
-void CameraWindows::add_active_cameras(){
+void CameraWindows::add_active_cameras() {
///@TODO scan through any active cameras and create CameraFeedWindows objects for them
-};
+}
CameraWindows::CameraWindows() {
// Find cameras active right now
@@ -92,7 +92,3 @@ CameraWindows::CameraWindows() {
// need to add something that will react to devices being connected/removed...
};
-
-CameraWindows::~CameraWindows(){
-
-};
diff --git a/modules/camera/camera_win.h b/modules/camera/camera_win.h
index 39a1b0b86f..bbc8880c12 100644
--- a/modules/camera/camera_win.h
+++ b/modules/camera/camera_win.h
@@ -40,7 +40,7 @@ private:
public:
CameraWindows();
- ~CameraWindows();
+ ~CameraWindows() {}
};
#endif /* CAMERAWIN_H */
diff --git a/modules/camera/register_types.h b/modules/camera/register_types.h
index f2753cb6d7..e34f84bf2c 100644
--- a/modules/camera/register_types.h
+++ b/modules/camera/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef CAMERA_REGISTER_TYPES_H
+#define CAMERA_REGISTER_TYPES_H
+
void register_camera_types();
void unregister_camera_types();
+
+#endif // CAMERA_REGISTER_TYPES_H
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index df798623f9..47982d519a 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -30,7 +30,7 @@
#include "csg.h"
-#include "core/math/geometry.h"
+#include "core/math/geometry_2d.h"
#include "core/math/math_funcs.h"
#include "core/sort_array.h"
@@ -154,6 +154,14 @@ inline bool is_point_in_triangle(const Vector3 &p_point, const Vector3 p_vertice
return true;
}
+inline static bool is_triangle_degenerate(const Vector2 p_vertices[3], real_t p_vertex_snap2) {
+ real_t det = p_vertices[0].x * p_vertices[1].y - p_vertices[0].x * p_vertices[2].y +
+ p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x +
+ p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x;
+
+ return det < p_vertex_snap2;
+}
+
inline static bool are_segements_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) {
Vector2 segment1 = p_segment1_points[1] - p_segment1_points[0];
Vector2 segment2 = p_segment2_points[1] - p_segment2_points[0];
@@ -523,7 +531,7 @@ void CSGBrushOperation::MeshMerge::_add_distance(List<real_t> &r_intersectionsA,
// Check if distance exists.
for (const List<real_t>::Element *E = intersections.front(); E; E = E->next()) {
- if (Math::abs(**E - p_distance) < vertex_snap) {
+ if (Math::is_equal_approx(**E, p_distance)) {
return;
}
}
@@ -583,8 +591,8 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de
// Check if faces are co-planar.
if ((current_normal - face_normal).length_squared() < CMP_EPSILON2 &&
is_point_in_triangle(face_center, current_points)) {
- // Only add an intersection if checking a B face.
- if (face.from_b) {
+ // Only add an intersection if not a B face.
+ if (!face.from_b) {
_add_distance(intersectionsA, intersectionsB, current_face.from_b, 0);
}
} else if (ray_intersects_triangle(face_center, face_normal, current_points, CMP_EPSILON, intersection_point)) {
@@ -904,8 +912,12 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
vertices[p_segment_indices[closest_idx]].point
};
if (are_segements_parallel(edge1, edge2, vertex_snap2)) {
- degenerate_points.push_back(outer_edge_idx[0]);
- degenerate_points.push_back(outer_edge_idx[1]);
+ if (!degenerate_points.find(outer_edge_idx[0])) {
+ degenerate_points.push_back(outer_edge_idx[0]);
+ }
+ if (!degenerate_points.find(outer_edge_idx[1])) {
+ degenerate_points.push_back(outer_edge_idx[1]);
+ }
continue;
}
@@ -964,7 +976,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
face_points[face_edge_idx],
face_points[(face_edge_idx + 1) % 3]
};
- Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(point_2D, edge_points);
+ Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point_2D, edge_points);
if ((closest_point - point_2D).length_squared() < vertex_snap2) {
int opposite_vertex_idx = face.vertex_idx[(face_edge_idx + 2) % 3];
@@ -1028,7 +1040,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
// First check if the ends of the segment are on the edge.
bool on_edge = false;
for (int edge_point_idx = 0; edge_point_idx < 2; ++edge_point_idx) {
- intersection_point = Geometry::get_closest_point_to_segment_2d(p_segment_points[edge_point_idx], edge_points);
+ intersection_point = Geometry2D::get_closest_point_to_segment(p_segment_points[edge_point_idx], edge_points);
if ((intersection_point - p_segment_points[edge_point_idx]).length_squared() < vertex_snap2) {
on_edge = true;
break;
@@ -1036,7 +1048,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
}
// Else check if the segment intersects the edge.
- if (on_edge || Geometry::segment_intersects_segment_2d(p_segment_points[0], p_segment_points[1], edge_points[0], edge_points[1], &intersection_point)) {
+ if (on_edge || Geometry2D::segment_intersects_segment(p_segment_points[0], p_segment_points[1], edge_points[0], edge_points[1], &intersection_point)) {
// Check if intersection point is an edge point.
if ((intersection_point - edge_points[0]).length_squared() < vertex_snap2 ||
(intersection_point - edge_points[1]).length_squared() < vertex_snap2) {
@@ -1064,17 +1076,8 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
break;
}
- // Don't create degenerate triangles.
- Vector2 split_edge1[2] = { vertices[new_vertex_idx].point, edge_points[0] };
- Vector2 split_edge2[2] = { vertices[new_vertex_idx].point, edge_points[1] };
- Vector2 new_edge[2] = { vertices[new_vertex_idx].point, vertices[opposite_vertex_idx].point };
- if (are_segements_parallel(split_edge1, new_edge, vertex_snap2) &&
- are_segements_parallel(split_edge2, new_edge, vertex_snap2)) {
- break;
- }
-
// If opposite point is on the segemnt, add its index to segment indices too.
- Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(vertices[opposite_vertex_idx].point, p_segment_points);
+ Vector2 closest_point = Geometry2D::get_closest_point_to_segment(vertices[opposite_vertex_idx].point, p_segment_points);
if ((closest_point - vertices[opposite_vertex_idx].point).length_squared() < vertex_snap2) {
_add_vertex_idx_sorted(r_segment_indices, opposite_vertex_idx);
}
@@ -1122,6 +1125,11 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
face_vertices[2].uv
};
+ // Skip degenerate triangles.
+ if (is_triangle_degenerate(points, vertex_snap2)) {
+ continue;
+ }
+
// Check if point is existing face vertex.
for (int i = 0; i < 3; ++i) {
if ((p_point - face_vertices[i].point).length_squared() < vertex_snap2) {
@@ -1141,7 +1149,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
uvs[(face_edge_idx + 1) % 3]
};
- Vector2 closest_point = Geometry::get_closest_point_to_segment_2d(p_point, edge_points);
+ Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, edge_points);
if ((closest_point - p_point).length_squared() < vertex_snap2) {
on_edge = true;
@@ -1192,7 +1200,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
}
// If not on an edge, check if the point is inside the face.
- if (!on_edge && Geometry::is_point_in_triangle(p_point, face_vertices[0].point, face_vertices[1].point, face_vertices[2].point)) {
+ if (!on_edge && Geometry2D::is_point_in_triangle(p_point, face_vertices[0].point, face_vertices[1].point, face_vertices[2].point)) {
// Add the point as a new vertex.
Vertex2D new_vertex;
new_vertex.point = p_point;
@@ -1203,11 +1211,8 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
// The new vertex is the last vertex.
for (int i = 0; i < 3; ++i) {
// Don't create degenerate triangles.
- Vector2 edge[2] = { points[i], points[(i + 1) % 3] };
- Vector2 new_edge1[2] = { vertices[new_vertex_idx].point, points[i] };
- Vector2 new_edge2[2] = { vertices[new_vertex_idx].point, points[(i + 1) % 3] };
- if (are_segements_parallel(edge, new_edge1, vertex_snap2) &&
- are_segements_parallel(edge, new_edge2, vertex_snap2)) {
+ Vector2 new_points[3] = { points[i], points[(i + 1) % 3], vertices[new_vertex_idx].point };
+ if (is_triangle_degenerate(new_points, vertex_snap2)) {
continue;
}
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
index 9fa7dc1340..cce72770f5 100644
--- a/modules/csg/csg_gizmos.cpp
+++ b/modules/csg/csg_gizmos.cpp
@@ -120,7 +120,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca
CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs);
Vector3 ra, rb;
- Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
+ Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
float d = ra.x;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
@@ -139,7 +139,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca
Vector3 axis;
axis[p_idx] = 1.0;
Vector3 ra, rb;
- Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = ra[p_idx];
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
@@ -168,7 +168,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca
Vector3 axis;
axis[p_idx == 0 ? 0 : 1] = 1.0;
Vector3 ra, rb;
- Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = axis.dot(ra);
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
@@ -191,7 +191,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca
Vector3 axis;
axis[0] = 1.0;
Vector3 ra, rb;
- Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = axis.dot(ra);
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h
index 48a414d9c7..83ee847caf 100644
--- a/modules/csg/csg_gizmos.h
+++ b/modules/csg/csg_gizmos.h
@@ -39,16 +39,16 @@ class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CSGShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
public:
- bool has_gizmo(Node3D *p_spatial);
- String get_name() const;
- int get_priority() const;
- bool is_selectable_when_hidden() const;
- void redraw(EditorNode3DGizmo *p_gizmo);
-
- String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const;
- Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const;
- void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point);
- void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel);
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_name() const override;
+ int get_priority() const override;
+ bool is_selectable_when_hidden() const override;
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override;
+ Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override;
+ void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override;
+ void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) override;
CSGShape3DGizmoPlugin();
};
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 7df65b04c4..8f2ebc7232 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "csg_shape.h"
+#include "core/math/geometry_2d.h"
#include "scene/3d/path_3d.h"
void CSGShape3D::set_use_collision(bool p_enable) {
@@ -131,18 +132,13 @@ void CSGShape3D::_make_dirty() {
return;
}
- if (dirty) {
- return;
- }
-
- dirty = true;
-
if (parent) {
parent->_make_dirty();
- } else {
- //only parent will do
+ } else if (!dirty) {
call_deferred("_update_shape");
}
+
+ dirty = true;
}
CSGBrush *CSGShape3D::_get_brush() {
@@ -510,6 +506,12 @@ void CSGShape3D::_notification(int p_what) {
_make_dirty();
}
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+ if (use_collision && is_root_shape() && root_collision_instance.is_valid()) {
+ PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
+ }
+ }
+
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
if (parent) {
parent->_make_dirty();
@@ -645,7 +647,7 @@ CSGShape3D::~CSGShape3D() {
//////////////////////////////////
CSGBrush *CSGCombiner3D::_build_brush() {
- return nullptr; //does not build anything
+ return memnew(CSGBrush); //does not build anything
}
CSGCombiner3D::CSGCombiner3D() {
@@ -1728,7 +1730,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
final_polygon.invert();
}
- Vector<int> triangles = Geometry::triangulate_polygon(final_polygon);
+ Vector<int> triangles = Geometry2D::triangulate_polygon(final_polygon);
if (triangles.size() < 3) {
return nullptr;
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 7e95d685c5..d93693f145 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -114,7 +114,7 @@ protected:
friend class CSGCombiner3D;
CSGBrush *_get_brush();
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
public:
Array get_meshes() const;
@@ -124,8 +124,8 @@ public:
virtual Vector<Vector3> get_brush_faces();
- virtual AABB get_aabb() const;
- virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const;
+ virtual AABB get_aabb() const override;
+ virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override;
void set_use_collision(bool p_enable);
bool is_using_collision() const;
@@ -159,7 +159,7 @@ class CSGCombiner3D : public CSGShape3D {
GDCLASS(CSGCombiner3D, CSGShape3D);
private:
- virtual CSGBrush *_build_brush();
+ virtual CSGBrush *_build_brush() override;
public:
CSGCombiner3D();
@@ -185,7 +185,7 @@ public:
class CSGMesh3D : public CSGPrimitive3D {
GDCLASS(CSGMesh3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush();
+ virtual CSGBrush *_build_brush() override;
Ref<Mesh> mesh;
Ref<Material> material;
@@ -205,7 +205,7 @@ public:
class CSGSphere3D : public CSGPrimitive3D {
GDCLASS(CSGSphere3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush();
+ virtual CSGBrush *_build_brush() override;
Ref<Material> material;
bool smooth_faces;
@@ -237,7 +237,7 @@ public:
class CSGBox3D : public CSGPrimitive3D {
GDCLASS(CSGBox3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush();
+ virtual CSGBrush *_build_brush() override;
Ref<Material> material;
float width;
@@ -265,7 +265,7 @@ public:
class CSGCylinder3D : public CSGPrimitive3D {
GDCLASS(CSGCylinder3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush();
+ virtual CSGBrush *_build_brush() override;
Ref<Material> material;
float radius;
@@ -301,7 +301,7 @@ public:
class CSGTorus3D : public CSGPrimitive3D {
GDCLASS(CSGTorus3D, CSGPrimitive3D);
- virtual CSGBrush *_build_brush();
+ virtual CSGBrush *_build_brush() override;
Ref<Material> material;
float inner_radius;
@@ -352,7 +352,7 @@ public:
};
private:
- virtual CSGBrush *_build_brush();
+ virtual CSGBrush *_build_brush() override;
Vector<Vector2> polygon;
Ref<Material> material;
@@ -383,7 +383,7 @@ private:
protected:
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
void _notification(int p_what);
public:
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index 43ce988c30..dac556c7f1 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -71,10 +71,10 @@
<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
The physics layers this area is in.
Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property.
- A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A.
+ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The physics layers this CSG shape scans for collisions.
+ The physics layers this CSG shape scans for collisions. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h
index 8472980c6a..36b5e332d6 100644
--- a/modules/cvtt/register_types.h
+++ b/modules/cvtt/register_types.h
@@ -28,14 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef TOOLS_ENABLED
-
#ifndef CVTT_REGISTER_TYPES_H
#define CVTT_REGISTER_TYPES_H
+#ifdef TOOLS_ENABLED
+
void register_cvtt_types();
void unregister_cvtt_types();
-#endif // CVTT_REGISTER_TYPES_H
-
#endif // TOOLS_ENABLED
+
+#endif // CVTT_REGISTER_TYPES_H
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index ba425371a8..2f4f7d7a4c 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -29,14 +29,15 @@
/*************************************************************************/
#include "texture_loader_dds.h"
+
#include "core/os/file_access.h"
#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
+// Reference: https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
+
enum {
DDS_MAGIC = 0x20534444,
- DDSD_CAPS = 0x00000001,
- DDSD_PIXELFORMAT = 0x00001000,
DDSD_PITCH = 0x00000008,
DDSD_LINEARSIZE = 0x00080000,
DDSD_MIPMAPCOUNT = 0x00020000,
@@ -47,7 +48,6 @@ enum {
};
enum DDSFormat {
-
DDS_DXT1,
DDS_DXT3,
DDS_DXT5,
@@ -128,7 +128,9 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
//validate
- if (magic != DDS_MAGIC || hsize != 124 || !(flags & DDSD_PIXELFORMAT) || !(flags & DDSD_CAPS)) {
+ // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
+ // but non-mandatory when reading (as some writers don't set them)...
+ if (magic != DDS_MAGIC || hsize != 124) {
ERR_FAIL_V_MSG(RES(), "Invalid or unsupported DDS texture file '" + p_path + "'.");
}
diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub
index 0fa65c6296..bf3bd7d073 100644
--- a/modules/denoise/SCsub
+++ b/modules/denoise/SCsub
@@ -1,7 +1,6 @@
#!/usr/bin/env python
import resource_to_cpp
-from platform_methods import run_in_subprocess
Import("env")
Import("env_modules")
diff --git a/modules/denoise/config.py b/modules/denoise/config.py
index 53b8f2f2e3..49a1f036ed 100644
--- a/modules/denoise/config.py
+++ b/modules/denoise/config.py
@@ -1,5 +1,11 @@
def can_build(env, platform):
- return env["tools"]
+ # Thirdparty dependency OpenImage Denoise includes oneDNN library
+ # which only supports 64-bit architectures.
+ # It's also only relevant for tools build and desktop platforms,
+ # as doing lightmap generation and denoising on Android or HTML5
+ # would be a bit far-fetched.
+ desktop_platforms = ["linuxbsd", "osx", "windows"]
+ return env["tools"] and platform in desktop_platforms and env["bits"] == "64" and env["arch"] != "arm64"
def configure(env):
diff --git a/modules/denoise/lightmap_denoiser.h b/modules/denoise/lightmap_denoiser.h
index 5b6e257df8..d01bbd10a5 100644
--- a/modules/denoise/lightmap_denoiser.h
+++ b/modules/denoise/lightmap_denoiser.h
@@ -45,7 +45,7 @@ protected:
public:
static LightmapDenoiser *create_oidn_denoiser();
- Ref<Image> denoise_image(const Ref<Image> &p_image);
+ Ref<Image> denoise_image(const Ref<Image> &p_image) override;
static void make_default_denoiser();
diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h
index 2ffc36ee2c..f0f1f44bfe 100644
--- a/modules/denoise/register_types.h
+++ b/modules/denoise/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef DENOISE_REGISTER_TYPES_H
+#define DENOISE_REGISTER_TYPES_H
+
void register_denoise_types();
void unregister_denoise_types();
+
+#endif // DENOISE_REGISTER_TYPES_H
diff --git a/modules/denoise/resource_to_cpp.py b/modules/denoise/resource_to_cpp.py
index 4c0b67f701..6c83277355 100644
--- a/modules/denoise/resource_to_cpp.py
+++ b/modules/denoise/resource_to_cpp.py
@@ -17,8 +17,6 @@
## ======================================================================== ##
import os
-import sys
-import argparse
from array import array
# Generates a C++ file from the specified binary resource file
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
index 860da32a22..f46ef2d812 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -7,8 +7,8 @@
A PacketPeer implementation that should be passed to [member SceneTree.network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
- <link>http://enet.bespin.org/usergroup0.html</link>
+ <link title="High-level multiplayer">https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
+ <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link>
</tutorials>
<methods>
<method name="close_connection">
@@ -128,7 +128,7 @@
Enforce ordered packets when using [constant NetworkedMultiplayerPeer.TRANSFER_MODE_UNRELIABLE] (thus behaving similarly to [constant NetworkedMultiplayerPeer.TRANSFER_MODE_UNRELIABLE_ORDERED]). This is the only way to use ordering with the RPC system.
</member>
<member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count" default="3">
- The number of channels to be used by ENet. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per channel basis.
+ The number of channels to be used by ENet. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per-channel basis. This is done to combat latency and reduces ordering restrictions on packets. The delivery status of a packet in one channel won't stall the delivery of other packets in another channel.
</member>
<member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode" default="0">
The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
@@ -141,7 +141,7 @@
Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
</member>
<member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel" default="-1">
- Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data.
+ Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels: one for reliable packets, and one for unreliable packets. The channel [code]0[/code] is reserved and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. See [member channel_count] for more information about ENet channels.
</member>
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" />
<member name="use_dtls" type="bool" setter="set_dtls_enabled" getter="is_dtls_enabled" default="false">
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 4e7698b67c..64977ad237 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -104,6 +104,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
if (dtls_enabled) {
enet_host_dtls_server_setup(host, dtls_key.ptr(), dtls_cert.ptr());
}
+ enet_host_refuse_new_connections(host, refuse_connections);
#endif
_setup_compressor();
@@ -160,6 +161,7 @@ Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_por
if (dtls_enabled) {
enet_host_dtls_client_setup(host, dtls_cert.ptr(), dtls_verify, p_address.utf8().get_data());
}
+ enet_host_refuse_new_connections(host, refuse_connections);
#endif
_setup_compressor();
@@ -640,6 +642,11 @@ int NetworkedMultiplayerENet::get_unique_id() const {
void NetworkedMultiplayerENet::set_refuse_new_connections(bool p_enable) {
refuse_connections = p_enable;
+#ifdef GODOT_ENET
+ if (active) {
+ enet_host_refuse_new_connections(host, p_enable);
+ }
+#endif
}
bool NetworkedMultiplayerENet::is_refusing_new_connections() const {
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index b2ed951327..722c7001fd 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -119,11 +119,11 @@ protected:
static void _bind_methods();
public:
- virtual void set_transfer_mode(TransferMode p_mode);
- virtual TransferMode get_transfer_mode() const;
- virtual void set_target_peer(int p_peer);
+ virtual void set_transfer_mode(TransferMode p_mode) override;
+ virtual TransferMode get_transfer_mode() const override;
+ virtual void set_target_peer(int p_peer) override;
- virtual int get_packet_peer() const;
+ virtual int get_packet_peer() const override;
virtual IP_Address get_peer_address(int p_peer_id) const;
virtual int get_peer_port(int p_peer_id) const;
@@ -135,22 +135,22 @@ public:
void disconnect_peer(int p_peer, bool now = false);
- virtual void poll();
+ virtual void poll() override;
- virtual bool is_server() const;
+ virtual bool is_server() const override;
- virtual int get_available_packet_count() const;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const;
+ virtual int get_max_packet_size() const override;
- virtual ConnectionStatus get_connection_status() const;
+ virtual ConnectionStatus get_connection_status() const override;
- virtual void set_refuse_new_connections(bool p_enable);
- virtual bool is_refusing_new_connections() const;
+ virtual void set_refuse_new_connections(bool p_enable) override;
+ virtual bool is_refusing_new_connections() const override;
- virtual int get_unique_id() const;
+ virtual int get_unique_id() const override;
void set_compression_mode(CompressionMode p_mode);
CompressionMode get_compression_mode() const;
diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp
index 9b6d8a2d35..d1ba3dc355 100644
--- a/modules/etc/image_etc.cpp
+++ b/modules/etc/image_etc.cpp
@@ -106,7 +106,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f
// If VRAM compression is using ETC, but image has alpha, convert to RGBA4444 or LA8
// This saves space while maintaining the alpha channel
if (detected_channels == Image::USED_CHANNELS_RGBA) {
-
+
if (p_img->has_mipmaps()) {
// Image doesn't support mipmaps with RGBA4444 textures
p_img->clear_mipmaps();
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index cab05549d2..0e2291c1f9 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -22,13 +22,12 @@ SConscript("pluginscript/SCsub")
SConscript("videodecoder/SCsub")
-from platform_methods import run_in_subprocess
import gdnative_builders
_, gensource = env_gdnative.CommandNoCache(
["include/gdnative_api_struct.gen.h", "gdnative_api_struct.gen.cpp"],
"gdnative_api.json",
- run_in_subprocess(gdnative_builders.build_gdnative_api_struct),
+ env.Run(gdnative_builders.build_gdnative_api_struct, "Generating GDNative API."),
)
env_gdnative.add_source_files(env.modules_sources, [gensource])
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index 4b997e4bfe..7603e7d69d 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -8,7 +8,6 @@ def configure(env):
def get_doc_classes():
return [
- "@NativeScript",
"XRInterfaceGDNative",
"GDNative",
"GDNativeLibrary",
diff --git a/modules/gdnative/doc_classes/@NativeScript.xml b/modules/gdnative/doc_classes/@NativeScript.xml
deleted file mode 100644
index 809b225e21..0000000000
--- a/modules/gdnative/doc_classes/@NativeScript.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@NativeScript" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index 1aab864102..05cda05f9f 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -7,8 +7,8 @@
A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [XRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link>
- <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link>
+ <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link>
+ <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link>
</tutorials>
<methods>
<method name="get_current_dependencies" qualifiers="const">
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index 3d747ba41e..bb2da70c3a 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -291,8 +291,26 @@ bool GDNative::initialize() {
return false;
}
#ifdef IPHONE_ENABLED
- // on iOS we use static linking
+ // On iOS we use static linking by default.
String path = "";
+
+ // On iOS dylibs is not allowed, but can be replaced with .framework or .xcframework.
+ // If they are used, we can run dlopen on them.
+ // They should be located under Frameworks directory, so we need to replace library path.
+ if (!lib_path.ends_with(".a")) {
+ path = ProjectSettings::get_singleton()->globalize_path(lib_path);
+
+ if (!FileAccess::exists(path)) {
+ String lib_name = lib_path.get_basename().get_file();
+ String framework_path_format = "Frameworks/$name.framework/$name";
+
+ Dictionary format_dict;
+ format_dict["name"] = lib_name;
+ String framework_path = framework_path_format.format(format_dict, "$_");
+
+ path = OS::get_singleton()->get_executable_path().get_base_dir().plus_file(framework_path);
+ }
+ }
#elif defined(ANDROID_ENABLED)
// On Android dynamic libraries are located separately from resource assets,
// we should pass library name to dlopen(). The library name is flattened
diff --git a/modules/gdnative/gdnative/aabb.cpp b/modules/gdnative/gdnative/aabb.cpp
index 246e5d4e8d..d5970e8004 100644
--- a/modules/gdnative/gdnative/aabb.cpp
+++ b/modules/gdnative/gdnative/aabb.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_aabb) == sizeof(AABB), "AABB size mismatch");
+
void GDAPI godot_aabb_new(godot_aabb *r_dest, const godot_vector3 *p_pos, const godot_vector3 *p_size) {
const Vector3 *pos = (const Vector3 *)p_pos;
const Vector3 *size = (const Vector3 *)p_size;
@@ -79,6 +81,13 @@ godot_string GDAPI godot_aabb_as_string(const godot_aabb *p_self) {
return ret;
}
+godot_aabb GDAPI godot_aabb_abs(const godot_aabb *p_self) {
+ godot_aabb dest;
+ const AABB *self = (const AABB *)p_self;
+ *((AABB *)&dest) = self->abs();
+ return dest;
+}
+
godot_real GDAPI godot_aabb_get_area(const godot_aabb *p_self) {
const AABB *self = (const AABB *)p_self;
return self->get_area();
diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp
index 0c764ab8fd..59953f5182 100644
--- a/modules/gdnative/gdnative/array.cpp
+++ b/modules/gdnative/gdnative/array.cpp
@@ -41,6 +41,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_array) == sizeof(Array), "Array size mismatch");
+
void GDAPI godot_array_new(godot_array *r_dest) {
Array *dest = (Array *)r_dest;
memnew_placement(dest, Array);
@@ -100,9 +102,33 @@ void GDAPI godot_array_new_packed_string_array(godot_array *r_dest, const godot_
}
}
-void GDAPI godot_array_new_packed_real_array(godot_array *r_dest, const godot_packed_real_array *p_pra) {
+void GDAPI godot_array_new_packed_float32_array(godot_array *r_dest, const godot_packed_float32_array *p_pra) {
+ Array *dest = (Array *)r_dest;
+ Vector<float> *pca = (Vector<float> *)p_pra;
+ memnew_placement(dest, Array);
+ dest->resize(pca->size());
+
+ for (int i = 0; i < dest->size(); i++) {
+ Variant v = pca->operator[](i);
+ dest->operator[](i) = v;
+ }
+}
+
+void GDAPI godot_array_new_packed_float64_array(godot_array *r_dest, const godot_packed_float64_array *p_pra) {
+ Array *dest = (Array *)r_dest;
+ Vector<double> *pca = (Vector<double> *)p_pra;
+ memnew_placement(dest, Array);
+ dest->resize(pca->size());
+
+ for (int i = 0; i < dest->size(); i++) {
+ Variant v = pca->operator[](i);
+ dest->operator[](i) = v;
+ }
+}
+
+void GDAPI godot_array_new_packed_int32_array(godot_array *r_dest, const godot_packed_int32_array *p_pia) {
Array *dest = (Array *)r_dest;
- Vector<godot_real> *pca = (Vector<godot_real> *)p_pra;
+ Vector<int32_t> *pca = (Vector<int32_t> *)p_pia;
memnew_placement(dest, Array);
dest->resize(pca->size());
@@ -112,9 +138,9 @@ void GDAPI godot_array_new_packed_real_array(godot_array *r_dest, const godot_pa
}
}
-void GDAPI godot_array_new_packed_int_array(godot_array *r_dest, const godot_packed_int_array *p_pia) {
+void GDAPI godot_array_new_packed_int64_array(godot_array *r_dest, const godot_packed_int64_array *p_pia) {
Array *dest = (Array *)r_dest;
- Vector<godot_int> *pca = (Vector<godot_int> *)p_pia;
+ Vector<int64_t> *pca = (Vector<int64_t> *)p_pia;
memnew_placement(dest, Array);
dest->resize(pca->size());
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index 4f489287b9..990fd3795d 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_basis) == sizeof(Basis), "Basis size mismatch");
+
void GDAPI godot_basis_new_with_rows(godot_basis *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis) {
const Vector3 *x_axis = (const Vector3 *)p_x_axis;
const Vector3 *y_axis = (const Vector3 *)p_y_axis;
diff --git a/modules/gdnative/gdnative/callable.cpp b/modules/gdnative/gdnative/callable.cpp
new file mode 100644
index 0000000000..868b324227
--- /dev/null
+++ b/modules/gdnative/gdnative/callable.cpp
@@ -0,0 +1,252 @@
+/*************************************************************************/
+/* callable.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "gdnative/callable.h"
+
+#include "core/callable.h"
+#include "core/resource.h"
+#include "core/variant.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static_assert(sizeof(godot_callable) == sizeof(Callable), "Callable size mismatch");
+static_assert(sizeof(godot_signal) == sizeof(Signal), "Signal size mismatch");
+
+// Callable
+
+void GDAPI godot_callable_new_with_object(godot_callable *r_dest, const godot_object *p_object, const godot_string_name *p_method) {
+ Callable *dest = (Callable *)r_dest;
+ const Object *object = (const Object *)p_object;
+ const StringName *method = (const StringName *)p_method;
+ memnew_placement(dest, Callable(object, *method));
+}
+
+void GDAPI godot_callable_new_with_object_id(godot_callable *r_dest, uint64_t p_objectid, const godot_string_name *p_method) {
+ Callable *dest = (Callable *)r_dest;
+ const StringName *method = (const StringName *)p_method;
+ memnew_placement(dest, Callable(ObjectID(p_objectid), *method));
+}
+
+void GDAPI godot_callable_new_copy(godot_callable *r_dest, const godot_callable *p_src) {
+ Callable *dest = (Callable *)r_dest;
+ const Callable *src = (const Callable *)p_src;
+ memnew_placement(dest, Callable(*src));
+}
+
+void GDAPI godot_callable_destroy(godot_callable *p_self) {
+ Callable *self = (Callable *)p_self;
+ self->~Callable();
+}
+
+godot_int GDAPI godot_callable_call(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount, godot_variant *r_return_value) {
+ const Callable *self = (const Callable *)p_self;
+ const Variant **arguments = (const Variant **)p_arguments;
+ Variant *return_value = (Variant *)r_return_value;
+ Variant ret;
+ Callable::CallError err;
+ self->call(arguments, p_argcount, ret, err);
+ if (return_value)
+ (*return_value) = ret;
+ return (godot_int)err.error;
+}
+
+void GDAPI godot_callable_call_deferred(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount) {
+ const Callable *self = (const Callable *)p_self;
+ const Variant **arguments = (const Variant **)p_arguments;
+ self->call_deferred(arguments, p_argcount);
+}
+
+godot_bool GDAPI godot_callable_is_null(const godot_callable *p_self) {
+ const Callable *self = (const Callable *)p_self;
+ return self->is_null();
+}
+
+godot_bool GDAPI godot_callable_is_custom(const godot_callable *p_self) {
+ const Callable *self = (const Callable *)p_self;
+ return self->is_custom();
+}
+
+godot_bool GDAPI godot_callable_is_standard(const godot_callable *p_self) {
+ const Callable *self = (const Callable *)p_self;
+ return self->is_standard();
+}
+
+godot_object GDAPI *godot_callable_get_object(const godot_callable *p_self) {
+ const Callable *self = (const Callable *)p_self;
+ return (godot_object *)self->get_object();
+}
+
+uint64_t GDAPI godot_callable_get_object_id(const godot_callable *p_self) {
+ const Callable *self = (const Callable *)p_self;
+ return (uint64_t)self->get_object_id();
+}
+
+godot_string_name GDAPI godot_callable_get_method(const godot_callable *p_self) {
+ godot_string_name raw_dest;
+ const Callable *self = (const Callable *)p_self;
+ StringName *dest = (StringName *)&raw_dest;
+ memnew_placement(dest, StringName(self->get_method()));
+ return raw_dest;
+}
+
+uint32_t GDAPI godot_callable_hash(const godot_callable *p_self) {
+ const Callable *self = (const Callable *)p_self;
+ return self->hash();
+}
+
+godot_string GDAPI godot_callable_as_string(const godot_callable *p_self) {
+ godot_string ret;
+ const Callable *self = (const Callable *)p_self;
+ memnew_placement(&ret, String(*self));
+ return ret;
+}
+
+godot_bool GDAPI godot_callable_operator_equal(const godot_callable *p_self, const godot_callable *p_other) {
+ const Callable *self = (const Callable *)p_self;
+ const Callable *other = (const Callable *)p_other;
+ return *self == *other;
+}
+
+godot_bool GDAPI godot_callable_operator_less(const godot_callable *p_self, const godot_callable *p_other) {
+ const Callable *self = (const Callable *)p_self;
+ const Callable *other = (const Callable *)p_other;
+ return *self < *other;
+}
+
+// Signal
+
+void GDAPI godot_signal_new_with_object(godot_signal *r_dest, const godot_object *p_object, const godot_string_name *p_name) {
+ Signal *dest = (Signal *)r_dest;
+ const Object *object = (const Object *)p_object;
+ const StringName *name = (const StringName *)p_name;
+ memnew_placement(dest, Signal(object, *name));
+}
+
+void GDAPI godot_signal_new_with_object_id(godot_signal *r_dest, uint64_t p_objectid, const godot_string_name *p_name) {
+ Signal *dest = (Signal *)r_dest;
+ const StringName *name = (const StringName *)p_name;
+ memnew_placement(dest, Signal(ObjectID(p_objectid), *name));
+}
+
+void GDAPI godot_signal_new_copy(godot_signal *r_dest, const godot_signal *p_src) {
+ Signal *dest = (Signal *)r_dest;
+ const Signal *src = (const Signal *)p_src;
+ memnew_placement(dest, Signal(*src));
+}
+
+void GDAPI godot_signal_destroy(godot_signal *p_self) {
+ Signal *self = (Signal *)p_self;
+ self->~Signal();
+}
+
+godot_int GDAPI godot_signal_emit(const godot_signal *p_self, const godot_variant **p_arguments, godot_int p_argcount) {
+ const Signal *self = (const Signal *)p_self;
+ const Variant **arguments = (const Variant **)p_arguments;
+ return (godot_int)self->emit(arguments, p_argcount);
+}
+
+godot_int GDAPI godot_signal_connect(godot_signal *p_self, const godot_callable *p_callable, const godot_array *p_binds, uint32_t p_flags) {
+ Signal *self = (Signal *)p_self;
+ const Callable *callable = (const Callable *)p_callable;
+ const Array *binds_ar = (const Array *)p_binds;
+ Vector<Variant> binds;
+ for (int i = 0; i < binds_ar->size(); i++) {
+ binds.push_back(binds_ar->get(i));
+ }
+ return (godot_int)self->connect(*callable, binds, p_flags);
+}
+
+void GDAPI godot_signal_disconnect(godot_signal *p_self, const godot_callable *p_callable) {
+ Signal *self = (Signal *)p_self;
+ const Callable *callable = (const Callable *)p_callable;
+ self->disconnect(*callable);
+}
+
+godot_bool GDAPI godot_signal_is_null(const godot_signal *p_self) {
+ const Signal *self = (const Signal *)p_self;
+ return self->is_null();
+}
+
+godot_bool GDAPI godot_signal_is_connected(const godot_signal *p_self, const godot_callable *p_callable) {
+ const Signal *self = (const Signal *)p_self;
+ const Callable *callable = (const Callable *)p_callable;
+ return self->is_connected(*callable);
+}
+
+godot_array GDAPI godot_signal_get_connections(const godot_signal *p_self) {
+ godot_array raw_dest;
+ const Signal *self = (const Signal *)p_self;
+ Array *dest = (Array *)&raw_dest;
+ memnew_placement(dest, Array(self->get_connections()));
+ return raw_dest;
+}
+
+godot_object GDAPI *godot_signal_get_object(const godot_signal *p_self) {
+ const Signal *self = (const Signal *)p_self;
+ return (godot_object *)self->get_object();
+}
+
+uint64_t GDAPI godot_signal_get_object_id(const godot_signal *p_self) {
+ const Signal *self = (const Signal *)p_self;
+ return (uint64_t)self->get_object_id();
+}
+
+godot_string_name GDAPI godot_signal_get_name(const godot_signal *p_self) {
+ godot_string_name raw_dest;
+ const Signal *self = (const Signal *)p_self;
+ StringName *dest = (StringName *)&raw_dest;
+ memnew_placement(dest, StringName(self->get_name()));
+ return raw_dest;
+}
+
+godot_string GDAPI godot_signal_as_string(const godot_signal *p_self) {
+ godot_string ret;
+ const Signal *self = (const Signal *)p_self;
+ memnew_placement(&ret, String(*self));
+ return ret;
+}
+
+godot_bool GDAPI godot_signal_operator_equal(const godot_signal *p_self, const godot_signal *p_other) {
+ const Signal *self = (const Signal *)p_self;
+ const Signal *other = (const Signal *)p_other;
+ return *self == *other;
+}
+
+godot_bool GDAPI godot_signal_operator_less(const godot_signal *p_self, const godot_signal *p_other) {
+ const Signal *self = (const Signal *)p_self;
+ const Signal *other = (const Signal *)p_other;
+ return *self < *other;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp
index d79170771a..c75e74daba 100644
--- a/modules/gdnative/gdnative/color.cpp
+++ b/modules/gdnative/gdnative/color.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_color) == sizeof(Color), "Color size mismatch");
+
void GDAPI godot_color_new_rgba(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b, const godot_real p_a) {
Color *dest = (Color *)r_dest;
*dest = Color(p_r, p_g, p_b, p_a);
diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp
index b145b88934..a126974815 100644
--- a/modules/gdnative/gdnative/dictionary.cpp
+++ b/modules/gdnative/gdnative/dictionary.cpp
@@ -39,6 +39,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_dictionary) == sizeof(Dictionary), "Dictionary size mismatch");
+
void GDAPI godot_dictionary_new(godot_dictionary *r_dest) {
Dictionary *dest = (Dictionary *)r_dest;
memnew_placement(dest, Dictionary);
diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp
index 6b2b5b80a4..e94190b07b 100644
--- a/modules/gdnative/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative/gdnative.cpp
@@ -165,7 +165,7 @@ void _gdnative_report_loading_error(const godot_object *p_library, const char *p
_err_print_error("gdnative_init", library->get_current_library_path().utf8().ptr(), 0, message.utf8().ptr());
}
-godot_object GDAPI *godot_instance_from_id(godot_int p_instance_id) {
+godot_object GDAPI *godot_instance_from_id(uint64_t p_instance_id) {
return (godot_object *)ObjectDB::get_instance(ObjectID(p_instance_id));
}
@@ -184,6 +184,11 @@ godot_object *godot_object_cast_to(const godot_object *p_object, void *p_class_t
return o->is_class_ptr(p_class_tag) ? (godot_object *)o : nullptr;
}
+uint64_t GDAPI godot_object_get_instance_id(const godot_object *p_object) {
+ const Object *o = (const Object *)p_object;
+ return (uint64_t)o->get_instance_id();
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp
index 93f43835c8..88ed650ebe 100644
--- a/modules/gdnative/gdnative/node_path.cpp
+++ b/modules/gdnative/gdnative/node_path.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_node_path) == sizeof(NodePath), "NodePath size mismatch");
+
void GDAPI godot_node_path_new(godot_node_path *r_dest, const godot_string *p_from) {
NodePath *dest = (NodePath *)r_dest;
const String *from = (const String *)p_from;
diff --git a/modules/gdnative/gdnative/pool_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp
index 589b4d4dfe..de93c1d9b3 100644
--- a/modules/gdnative/gdnative/pool_arrays.cpp
+++ b/modules/gdnative/gdnative/packed_arrays.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* pool_arrays.cpp */
+/* packed_arrays.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "gdnative/pool_arrays.h"
+#include "gdnative/packed_arrays.h"
#include "core/array.h"
@@ -42,6 +42,16 @@
extern "C" {
#endif
+static_assert(sizeof(godot_packed_byte_array) == sizeof(Vector<uint8_t>), "Vector<uint8_t> size mismatch");
+static_assert(sizeof(godot_packed_int32_array) == sizeof(Vector<int32_t>), "Vector<int32_t> size mismatch");
+static_assert(sizeof(godot_packed_int64_array) == sizeof(Vector<int64_t>), "Vector<int64_t> size mismatch");
+static_assert(sizeof(godot_packed_float32_array) == sizeof(Vector<float>), "Vector<float> size mismatch");
+static_assert(sizeof(godot_packed_float64_array) == sizeof(Vector<double>), "Vector<double> size mismatch");
+static_assert(sizeof(godot_packed_string_array) == sizeof(Vector<String>), "Vector<String> size mismatch");
+static_assert(sizeof(godot_packed_vector2_array) == sizeof(Vector<Vector2>), "Vector<Vector2> size mismatch");
+static_assert(sizeof(godot_packed_vector3_array) == sizeof(Vector<Vector3>), "Vector<Vector3> size mismatch");
+static_assert(sizeof(godot_packed_color_array) == sizeof(Vector<Color>), "Vector<Color> size mismatch");
+
#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr)
// byte
@@ -68,6 +78,16 @@ void GDAPI godot_packed_byte_array_new_with_array(godot_packed_byte_array *r_des
}
}
+const uint8_t GDAPI *godot_packed_byte_array_ptr(const godot_packed_byte_array *p_self) {
+ const Vector<uint8_t> *self = (const Vector<uint8_t> *)p_self;
+ return self->ptr();
+}
+
+uint8_t GDAPI *godot_packed_byte_array_ptrw(godot_packed_byte_array *p_self) {
+ Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
+ return self->ptrw();
+}
+
void GDAPI godot_packed_byte_array_append(godot_packed_byte_array *p_self, const uint8_t p_data) {
Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
self->push_back(p_data);
@@ -84,6 +104,16 @@ godot_error GDAPI godot_packed_byte_array_insert(godot_packed_byte_array *p_self
return (godot_error)self->insert(p_idx, p_data);
}
+godot_bool GDAPI godot_packed_byte_array_has(godot_packed_byte_array *p_self, const uint8_t p_value) {
+ Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
+ return (godot_bool)self->has(p_value);
+}
+
+void GDAPI godot_packed_byte_array_sort(godot_packed_byte_array *p_self) {
+ Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
+ self->sort();
+}
+
void GDAPI godot_packed_byte_array_invert(godot_packed_byte_array *p_self) {
Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
self->invert();
@@ -128,23 +158,23 @@ void GDAPI godot_packed_byte_array_destroy(godot_packed_byte_array *p_self) {
((Vector<uint8_t> *)p_self)->~Vector();
}
-// int
+// int32
-void GDAPI godot_packed_int_array_new(godot_packed_int_array *r_dest) {
- Vector<godot_int> *dest = (Vector<godot_int> *)r_dest;
- memnew_placement(dest, Vector<godot_int>);
+void GDAPI godot_packed_int32_array_new(godot_packed_int32_array *r_dest) {
+ Vector<int32_t> *dest = (Vector<int32_t> *)r_dest;
+ memnew_placement(dest, Vector<int32_t>);
}
-void GDAPI godot_packed_int_array_new_copy(godot_packed_int_array *r_dest, const godot_packed_int_array *p_src) {
- Vector<godot_int> *dest = (Vector<godot_int> *)r_dest;
- const Vector<godot_int> *src = (const Vector<godot_int> *)p_src;
- memnew_placement(dest, Vector<godot_int>(*src));
+void GDAPI godot_packed_int32_array_new_copy(godot_packed_int32_array *r_dest, const godot_packed_int32_array *p_src) {
+ Vector<int32_t> *dest = (Vector<int32_t> *)r_dest;
+ const Vector<int32_t> *src = (const Vector<int32_t> *)p_src;
+ memnew_placement(dest, Vector<int32_t>(*src));
}
-void GDAPI godot_packed_int_array_new_with_array(godot_packed_int_array *r_dest, const godot_array *p_a) {
- Vector<godot_int> *dest = (Vector<godot_int> *)r_dest;
+void GDAPI godot_packed_int32_array_new_with_array(godot_packed_int32_array *r_dest, const godot_array *p_a) {
+ Vector<int32_t> *dest = (Vector<int32_t> *)r_dest;
Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<godot_int>);
+ memnew_placement(dest, Vector<int32_t>);
dest->resize(a->size());
for (int i = 0; i < a->size(); i++) {
@@ -152,83 +182,103 @@ void GDAPI godot_packed_int_array_new_with_array(godot_packed_int_array *r_dest,
}
}
-void GDAPI godot_packed_int_array_append(godot_packed_int_array *p_self, const godot_int p_data) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
+const int32_t GDAPI *godot_packed_int32_array_ptr(const godot_packed_int32_array *p_self) {
+ const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
+ return self->ptr();
+}
+
+int32_t GDAPI *godot_packed_int32_array_ptrw(godot_packed_int32_array *p_self) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
+ return self->ptrw();
+}
+
+void GDAPI godot_packed_int32_array_append(godot_packed_int32_array *p_self, const int32_t p_data) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
self->push_back(p_data);
}
-void GDAPI godot_packed_int_array_append_array(godot_packed_int_array *p_self, const godot_packed_int_array *p_array) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
- Vector<godot_int> *array = (Vector<godot_int> *)p_array;
+void GDAPI godot_packed_int32_array_append_array(godot_packed_int32_array *p_self, const godot_packed_int32_array *p_array) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
+ Vector<int32_t> *array = (Vector<int32_t> *)p_array;
self->append_array(*array);
}
-godot_error GDAPI godot_packed_int_array_insert(godot_packed_int_array *p_self, const godot_int p_idx, const godot_int p_data) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
+godot_error GDAPI godot_packed_int32_array_insert(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
return (godot_error)self->insert(p_idx, p_data);
}
-void GDAPI godot_packed_int_array_invert(godot_packed_int_array *p_self) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
+godot_bool GDAPI godot_packed_int32_array_has(godot_packed_int32_array *p_self, const int32_t p_value) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
+ return (godot_bool)self->has(p_value);
+}
+
+void GDAPI godot_packed_int32_array_sort(godot_packed_int32_array *p_self) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
+ self->sort();
+}
+
+void GDAPI godot_packed_int32_array_invert(godot_packed_int32_array *p_self) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
self->invert();
}
-void GDAPI godot_packed_int_array_push_back(godot_packed_int_array *p_self, const godot_int p_data) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
+void GDAPI godot_packed_int32_array_push_back(godot_packed_int32_array *p_self, const int32_t p_data) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
self->push_back(p_data);
}
-void GDAPI godot_packed_int_array_remove(godot_packed_int_array *p_self, const godot_int p_idx) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
+void GDAPI godot_packed_int32_array_remove(godot_packed_int32_array *p_self, const godot_int p_idx) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
self->remove(p_idx);
}
-void GDAPI godot_packed_int_array_resize(godot_packed_int_array *p_self, const godot_int p_size) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
+void GDAPI godot_packed_int32_array_resize(godot_packed_int32_array *p_self, const godot_int p_size) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
self->resize(p_size);
}
-void GDAPI godot_packed_int_array_set(godot_packed_int_array *p_self, const godot_int p_idx, const godot_int p_data) {
- Vector<godot_int> *self = (Vector<godot_int> *)p_self;
+void GDAPI godot_packed_int32_array_set(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data) {
+ Vector<int32_t> *self = (Vector<int32_t> *)p_self;
self->set(p_idx, p_data);
}
-godot_int GDAPI godot_packed_int_array_get(const godot_packed_int_array *p_self, const godot_int p_idx) {
- const Vector<godot_int> *self = (const Vector<godot_int> *)p_self;
+int32_t GDAPI godot_packed_int32_array_get(const godot_packed_int32_array *p_self, const godot_int p_idx) {
+ const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
return self->get(p_idx);
}
-godot_int GDAPI godot_packed_int_array_size(const godot_packed_int_array *p_self) {
- const Vector<godot_int> *self = (const Vector<godot_int> *)p_self;
+godot_int GDAPI godot_packed_int32_array_size(const godot_packed_int32_array *p_self) {
+ const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
return self->size();
}
-godot_bool GDAPI godot_packed_int_array_empty(const godot_packed_int_array *p_self) {
- const Vector<godot_int> *self = (const Vector<godot_int> *)p_self;
+godot_bool GDAPI godot_packed_int32_array_empty(const godot_packed_int32_array *p_self) {
+ const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
return self->empty();
}
-void GDAPI godot_packed_int_array_destroy(godot_packed_int_array *p_self) {
- ((Vector<godot_int> *)p_self)->~Vector();
+void GDAPI godot_packed_int32_array_destroy(godot_packed_int32_array *p_self) {
+ ((Vector<int32_t> *)p_self)->~Vector();
}
-// real
+// int64
-void GDAPI godot_packed_real_array_new(godot_packed_real_array *r_dest) {
- Vector<godot_real> *dest = (Vector<godot_real> *)r_dest;
- memnew_placement(dest, Vector<godot_real>);
+void GDAPI godot_packed_int64_array_new(godot_packed_int64_array *r_dest) {
+ Vector<int64_t> *dest = (Vector<int64_t> *)r_dest;
+ memnew_placement(dest, Vector<int64_t>);
}
-void GDAPI godot_packed_real_array_new_copy(godot_packed_real_array *r_dest, const godot_packed_real_array *p_src) {
- Vector<godot_real> *dest = (Vector<godot_real> *)r_dest;
- const Vector<godot_real> *src = (const Vector<godot_real> *)p_src;
- memnew_placement(dest, Vector<godot_real>(*src));
+void GDAPI godot_packed_int64_array_new_copy(godot_packed_int64_array *r_dest, const godot_packed_int64_array *p_src) {
+ Vector<int64_t> *dest = (Vector<int64_t> *)r_dest;
+ const Vector<int64_t> *src = (const Vector<int64_t> *)p_src;
+ memnew_placement(dest, Vector<int64_t>(*src));
}
-void GDAPI godot_packed_real_array_new_with_array(godot_packed_real_array *r_dest, const godot_array *p_a) {
- Vector<godot_real> *dest = (Vector<godot_real> *)r_dest;
+void GDAPI godot_packed_int64_array_new_with_array(godot_packed_int64_array *r_dest, const godot_array *p_a) {
+ Vector<int64_t> *dest = (Vector<int64_t> *)r_dest;
Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<godot_real>);
+ memnew_placement(dest, Vector<int64_t>);
dest->resize(a->size());
for (int i = 0; i < a->size(); i++) {
@@ -236,64 +286,292 @@ void GDAPI godot_packed_real_array_new_with_array(godot_packed_real_array *r_des
}
}
-void GDAPI godot_packed_real_array_append(godot_packed_real_array *p_self, const godot_real p_data) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
+const int64_t GDAPI *godot_packed_int64_array_ptr(const godot_packed_int64_array *p_self) {
+ const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
+ return self->ptr();
+}
+
+int64_t GDAPI *godot_packed_int64_array_ptrw(godot_packed_int64_array *p_self) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
+ return self->ptrw();
+}
+
+void GDAPI godot_packed_int64_array_append(godot_packed_int64_array *p_self, const int64_t p_data) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
self->push_back(p_data);
}
-void GDAPI godot_packed_real_array_append_array(godot_packed_real_array *p_self, const godot_packed_real_array *p_array) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
- Vector<godot_real> *array = (Vector<godot_real> *)p_array;
+void GDAPI godot_packed_int64_array_append_array(godot_packed_int64_array *p_self, const godot_packed_int64_array *p_array) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
+ Vector<int64_t> *array = (Vector<int64_t> *)p_array;
self->append_array(*array);
}
-godot_error GDAPI godot_packed_real_array_insert(godot_packed_real_array *p_self, const godot_int p_idx, const godot_real p_data) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
+godot_error GDAPI godot_packed_int64_array_insert(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
return (godot_error)self->insert(p_idx, p_data);
}
-void GDAPI godot_packed_real_array_invert(godot_packed_real_array *p_self) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
+godot_bool GDAPI godot_packed_int64_array_has(godot_packed_int64_array *p_self, const int64_t p_value) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
+ return (godot_bool)self->has(p_value);
+}
+
+void GDAPI godot_packed_int64_array_sort(godot_packed_int64_array *p_self) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
+ self->sort();
+}
+
+void GDAPI godot_packed_int64_array_invert(godot_packed_int64_array *p_self) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
self->invert();
}
-void GDAPI godot_packed_real_array_push_back(godot_packed_real_array *p_self, const godot_real p_data) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
+void GDAPI godot_packed_int64_array_push_back(godot_packed_int64_array *p_self, const int64_t p_data) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
self->push_back(p_data);
}
-void GDAPI godot_packed_real_array_remove(godot_packed_real_array *p_self, const godot_int p_idx) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
+void GDAPI godot_packed_int64_array_remove(godot_packed_int64_array *p_self, const godot_int p_idx) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
self->remove(p_idx);
}
-void GDAPI godot_packed_real_array_resize(godot_packed_real_array *p_self, const godot_int p_size) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
+void GDAPI godot_packed_int64_array_resize(godot_packed_int64_array *p_self, const godot_int p_size) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
self->resize(p_size);
}
-void GDAPI godot_packed_real_array_set(godot_packed_real_array *p_self, const godot_int p_idx, const godot_real p_data) {
- Vector<godot_real> *self = (Vector<godot_real> *)p_self;
+void GDAPI godot_packed_int64_array_set(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data) {
+ Vector<int64_t> *self = (Vector<int64_t> *)p_self;
self->set(p_idx, p_data);
}
-godot_real GDAPI godot_packed_real_array_get(const godot_packed_real_array *p_self, const godot_int p_idx) {
- const Vector<godot_real> *self = (const Vector<godot_real> *)p_self;
+int64_t GDAPI godot_packed_int64_array_get(const godot_packed_int64_array *p_self, const godot_int p_idx) {
+ const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
return self->get(p_idx);
}
-godot_int GDAPI godot_packed_real_array_size(const godot_packed_real_array *p_self) {
- const Vector<godot_real> *self = (const Vector<godot_real> *)p_self;
+godot_int GDAPI godot_packed_int64_array_size(const godot_packed_int64_array *p_self) {
+ const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
return self->size();
}
-godot_bool GDAPI godot_packed_real_array_empty(const godot_packed_real_array *p_self) {
- const Vector<godot_real> *self = (const Vector<godot_real> *)p_self;
+godot_bool GDAPI godot_packed_int64_array_empty(const godot_packed_int64_array *p_self) {
+ const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
return self->empty();
}
-void GDAPI godot_packed_real_array_destroy(godot_packed_real_array *p_self) {
- ((Vector<godot_real> *)p_self)->~Vector();
+void GDAPI godot_packed_int64_array_destroy(godot_packed_int64_array *p_self) {
+ ((Vector<int64_t> *)p_self)->~Vector();
+}
+
+// float32
+
+void GDAPI godot_packed_float32_array_new(godot_packed_float32_array *r_dest) {
+ Vector<float> *dest = (Vector<float> *)r_dest;
+ memnew_placement(dest, Vector<float>);
+}
+
+void GDAPI godot_packed_float32_array_new_copy(godot_packed_float32_array *r_dest, const godot_packed_float32_array *p_src) {
+ Vector<float> *dest = (Vector<float> *)r_dest;
+ const Vector<float> *src = (const Vector<float> *)p_src;
+ memnew_placement(dest, Vector<float>(*src));
+}
+
+void GDAPI godot_packed_float32_array_new_with_array(godot_packed_float32_array *r_dest, const godot_array *p_a) {
+ Vector<float> *dest = (Vector<float> *)r_dest;
+ Array *a = (Array *)p_a;
+ memnew_placement(dest, Vector<float>);
+
+ dest->resize(a->size());
+ for (int i = 0; i < a->size(); i++) {
+ dest->set(i, (*a)[i]);
+ }
+}
+
+const float GDAPI *godot_packed_float32_array_ptr(const godot_packed_float32_array *p_self) {
+ const Vector<float> *self = (const Vector<float> *)p_self;
+ return self->ptr();
+}
+
+float GDAPI *godot_packed_float32_array_ptrw(godot_packed_float32_array *p_self) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ return self->ptrw();
+}
+
+void GDAPI godot_packed_float32_array_append(godot_packed_float32_array *p_self, const float p_data) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ self->push_back(p_data);
+}
+
+void GDAPI godot_packed_float32_array_append_array(godot_packed_float32_array *p_self, const godot_packed_float32_array *p_array) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ Vector<float> *array = (Vector<float> *)p_array;
+ self->append_array(*array);
+}
+
+godot_error GDAPI godot_packed_float32_array_insert(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ return (godot_error)self->insert(p_idx, p_data);
+}
+
+godot_bool GDAPI godot_packed_float32_array_has(godot_packed_float32_array *p_self, const float p_value) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ return (godot_bool)self->has(p_value);
+}
+
+void GDAPI godot_packed_float32_array_sort(godot_packed_float32_array *p_self) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ self->sort();
+}
+
+void GDAPI godot_packed_float32_array_invert(godot_packed_float32_array *p_self) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ self->invert();
+}
+
+void GDAPI godot_packed_float32_array_push_back(godot_packed_float32_array *p_self, const float p_data) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ self->push_back(p_data);
+}
+
+void GDAPI godot_packed_float32_array_remove(godot_packed_float32_array *p_self, const godot_int p_idx) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ self->remove(p_idx);
+}
+
+void GDAPI godot_packed_float32_array_resize(godot_packed_float32_array *p_self, const godot_int p_size) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ self->resize(p_size);
+}
+
+void GDAPI godot_packed_float32_array_set(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data) {
+ Vector<float> *self = (Vector<float> *)p_self;
+ self->set(p_idx, p_data);
+}
+
+float GDAPI godot_packed_float32_array_get(const godot_packed_float32_array *p_self, const godot_int p_idx) {
+ const Vector<float> *self = (const Vector<float> *)p_self;
+ return self->get(p_idx);
+}
+
+godot_int GDAPI godot_packed_float32_array_size(const godot_packed_float32_array *p_self) {
+ const Vector<float> *self = (const Vector<float> *)p_self;
+ return self->size();
+}
+
+godot_bool GDAPI godot_packed_float32_array_empty(const godot_packed_float32_array *p_self) {
+ const Vector<float> *self = (const Vector<float> *)p_self;
+ return self->empty();
+}
+
+void GDAPI godot_packed_float32_array_destroy(godot_packed_float32_array *p_self) {
+ ((Vector<float> *)p_self)->~Vector();
+}
+
+// float64
+
+void GDAPI godot_packed_float64_array_new(godot_packed_float64_array *r_dest) {
+ Vector<double> *dest = (Vector<double> *)r_dest;
+ memnew_placement(dest, Vector<double>);
+}
+
+void GDAPI godot_packed_float64_array_new_copy(godot_packed_float64_array *r_dest, const godot_packed_float64_array *p_src) {
+ Vector<double> *dest = (Vector<double> *)r_dest;
+ const Vector<double> *src = (const Vector<double> *)p_src;
+ memnew_placement(dest, Vector<double>(*src));
+}
+
+void GDAPI godot_packed_float64_array_new_with_array(godot_packed_float64_array *r_dest, const godot_array *p_a) {
+ Vector<double> *dest = (Vector<double> *)r_dest;
+ Array *a = (Array *)p_a;
+ memnew_placement(dest, Vector<double>);
+
+ dest->resize(a->size());
+ for (int i = 0; i < a->size(); i++) {
+ dest->set(i, (*a)[i]);
+ }
+}
+
+const double GDAPI *godot_packed_float64_array_ptr(const godot_packed_float64_array *p_self) {
+ const Vector<double> *self = (const Vector<double> *)p_self;
+ return self->ptr();
+}
+
+double GDAPI *godot_packed_float64_array_ptrw(godot_packed_float64_array *p_self) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ return self->ptrw();
+}
+
+void GDAPI godot_packed_float64_array_append(godot_packed_float64_array *p_self, const double p_data) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ self->push_back(p_data);
+}
+
+void GDAPI godot_packed_float64_array_append_array(godot_packed_float64_array *p_self, const godot_packed_float64_array *p_array) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ Vector<double> *array = (Vector<double> *)p_array;
+ self->append_array(*array);
+}
+
+godot_error GDAPI godot_packed_float64_array_insert(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ return (godot_error)self->insert(p_idx, p_data);
+}
+
+godot_bool GDAPI godot_packed_float64_array_has(godot_packed_float64_array *p_self, const double p_value) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ return (godot_bool)self->has(p_value);
+}
+
+void GDAPI godot_packed_float64_array_sort(godot_packed_float64_array *p_self) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ self->sort();
+}
+
+void GDAPI godot_packed_float64_array_invert(godot_packed_float64_array *p_self) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ self->invert();
+}
+
+void GDAPI godot_packed_float64_array_push_back(godot_packed_float64_array *p_self, const double p_data) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ self->push_back(p_data);
+}
+
+void GDAPI godot_packed_float64_array_remove(godot_packed_float64_array *p_self, const godot_int p_idx) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ self->remove(p_idx);
+}
+
+void GDAPI godot_packed_float64_array_resize(godot_packed_float64_array *p_self, const godot_int p_size) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ self->resize(p_size);
+}
+
+void GDAPI godot_packed_float64_array_set(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data) {
+ Vector<double> *self = (Vector<double> *)p_self;
+ self->set(p_idx, p_data);
+}
+
+double GDAPI godot_packed_float64_array_get(const godot_packed_float64_array *p_self, const godot_int p_idx) {
+ const Vector<double> *self = (const Vector<double> *)p_self;
+ return self->get(p_idx);
+}
+
+godot_int GDAPI godot_packed_float64_array_size(const godot_packed_float64_array *p_self) {
+ const Vector<double> *self = (const Vector<double> *)p_self;
+ return self->size();
+}
+
+godot_bool GDAPI godot_packed_float64_array_empty(const godot_packed_float64_array *p_self) {
+ const Vector<double> *self = (const Vector<double> *)p_self;
+ return self->empty();
+}
+
+void GDAPI godot_packed_float64_array_destroy(godot_packed_float64_array *p_self) {
+ ((Vector<double> *)p_self)->~Vector();
}
// string
@@ -320,6 +598,16 @@ void GDAPI godot_packed_string_array_new_with_array(godot_packed_string_array *r
}
}
+const godot_string GDAPI *godot_packed_string_array_ptr(const godot_packed_string_array *p_self) {
+ const Vector<String> *self = (const Vector<String> *)p_self;
+ return (const godot_string *)self->ptr();
+}
+
+godot_string GDAPI *godot_packed_string_array_ptrw(godot_packed_string_array *p_self) {
+ Vector<String> *self = (Vector<String> *)p_self;
+ return (godot_string *)self->ptrw();
+}
+
void GDAPI godot_packed_string_array_append(godot_packed_string_array *p_self, const godot_string *p_data) {
Vector<String> *self = (Vector<String> *)p_self;
String &s = *(String *)p_data;
@@ -338,6 +626,17 @@ godot_error GDAPI godot_packed_string_array_insert(godot_packed_string_array *p_
return (godot_error)self->insert(p_idx, s);
}
+godot_bool GDAPI godot_packed_string_array_has(godot_packed_string_array *p_self, const godot_string *p_value) {
+ Vector<String> *self = (Vector<String> *)p_self;
+ String &s = *(String *)p_value;
+ return (godot_bool)self->has(s);
+}
+
+void GDAPI godot_packed_string_array_sort(godot_packed_string_array *p_self) {
+ Vector<String> *self = (Vector<String> *)p_self;
+ self->sort();
+}
+
void GDAPI godot_packed_string_array_invert(godot_packed_string_array *p_self) {
Vector<String> *self = (Vector<String> *)p_self;
self->invert();
@@ -412,6 +711,16 @@ void GDAPI godot_packed_vector2_array_new_with_array(godot_packed_vector2_array
}
}
+const godot_vector2 GDAPI *godot_packed_vector2_array_ptr(const godot_packed_vector2_array *p_self) {
+ const Vector<Vector2> *self = (const Vector<Vector2> *)p_self;
+ return (const godot_vector2 *)self->ptr();
+}
+
+godot_vector2 GDAPI *godot_packed_vector2_array_ptrw(godot_packed_vector2_array *p_self) {
+ Vector<Vector2> *self = (Vector<Vector2> *)p_self;
+ return (godot_vector2 *)self->ptrw();
+}
+
void GDAPI godot_packed_vector2_array_append(godot_packed_vector2_array *p_self, const godot_vector2 *p_data) {
Vector<Vector2> *self = (Vector<Vector2> *)p_self;
Vector2 &s = *(Vector2 *)p_data;
@@ -430,6 +739,17 @@ godot_error GDAPI godot_packed_vector2_array_insert(godot_packed_vector2_array *
return (godot_error)self->insert(p_idx, s);
}
+godot_bool GDAPI godot_packed_vector2_array_has(godot_packed_vector2_array *p_self, const godot_vector2 *p_value) {
+ Vector<Vector2> *self = (Vector<Vector2> *)p_self;
+ Vector2 &v = *(Vector2 *)p_value;
+ return (godot_bool)self->has(v);
+}
+
+void GDAPI godot_packed_vector2_array_sort(godot_packed_vector2_array *p_self) {
+ Vector<Vector2> *self = (Vector<Vector2> *)p_self;
+ self->sort();
+}
+
void GDAPI godot_packed_vector2_array_invert(godot_packed_vector2_array *p_self) {
Vector<Vector2> *self = (Vector<Vector2> *)p_self;
self->invert();
@@ -503,6 +823,16 @@ void GDAPI godot_packed_vector3_array_new_with_array(godot_packed_vector3_array
}
}
+const godot_vector3 GDAPI *godot_packed_vector3_array_ptr(const godot_packed_vector3_array *p_self) {
+ const Vector<Vector3> *self = (const Vector<Vector3> *)p_self;
+ return (const godot_vector3 *)self->ptr();
+}
+
+godot_vector3 GDAPI *godot_packed_vector3_array_ptrw(godot_packed_vector3_array *p_self) {
+ Vector<Vector3> *self = (Vector<Vector3> *)p_self;
+ return (godot_vector3 *)self->ptrw();
+}
+
void GDAPI godot_packed_vector3_array_append(godot_packed_vector3_array *p_self, const godot_vector3 *p_data) {
Vector<Vector3> *self = (Vector<Vector3> *)p_self;
Vector3 &s = *(Vector3 *)p_data;
@@ -521,6 +851,17 @@ godot_error GDAPI godot_packed_vector3_array_insert(godot_packed_vector3_array *
return (godot_error)self->insert(p_idx, s);
}
+godot_bool GDAPI godot_packed_vector3_array_has(godot_packed_vector3_array *p_self, const godot_vector3 *p_value) {
+ Vector<Vector3> *self = (Vector<Vector3> *)p_self;
+ Vector3 &v = *(Vector3 *)p_value;
+ return (godot_bool)self->has(v);
+}
+
+void GDAPI godot_packed_vector3_array_sort(godot_packed_vector3_array *p_self) {
+ Vector<Vector3> *self = (Vector<Vector3> *)p_self;
+ self->sort();
+}
+
void GDAPI godot_packed_vector3_array_invert(godot_packed_vector3_array *p_self) {
Vector<Vector3> *self = (Vector<Vector3> *)p_self;
self->invert();
@@ -594,6 +935,16 @@ void GDAPI godot_packed_color_array_new_with_array(godot_packed_color_array *r_d
}
}
+const godot_color GDAPI *godot_packed_color_array_ptr(const godot_packed_color_array *p_self) {
+ const Vector<Color> *self = (const Vector<Color> *)p_self;
+ return (const godot_color *)self->ptr();
+}
+
+godot_color GDAPI *godot_packed_color_array_ptrw(godot_packed_color_array *p_self) {
+ Vector<Color> *self = (Vector<Color> *)p_self;
+ return (godot_color *)self->ptrw();
+}
+
void GDAPI godot_packed_color_array_append(godot_packed_color_array *p_self, const godot_color *p_data) {
Vector<Color> *self = (Vector<Color> *)p_self;
Color &s = *(Color *)p_data;
@@ -612,6 +963,17 @@ godot_error GDAPI godot_packed_color_array_insert(godot_packed_color_array *p_se
return (godot_error)self->insert(p_idx, s);
}
+godot_bool GDAPI godot_packed_color_array_has(godot_packed_color_array *p_self, const godot_color *p_value) {
+ Vector<Color> *self = (Vector<Color> *)p_self;
+ Color &c = *(Color *)p_value;
+ return (godot_bool)self->has(c);
+}
+
+void GDAPI godot_packed_color_array_sort(godot_packed_color_array *p_self) {
+ Vector<Color> *self = (Vector<Color> *)p_self;
+ self->sort();
+}
+
void GDAPI godot_packed_color_array_invert(godot_packed_color_array *p_self) {
Vector<Color> *self = (Vector<Color> *)p_self;
self->invert();
diff --git a/modules/gdnative/gdnative/plane.cpp b/modules/gdnative/gdnative/plane.cpp
index 923308dc34..d4ed8d00f4 100644
--- a/modules/gdnative/gdnative/plane.cpp
+++ b/modules/gdnative/gdnative/plane.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_plane) == sizeof(Plane), "Plane size mismatch");
+
void GDAPI godot_plane_new_with_reals(godot_plane *r_dest, const godot_real p_a, const godot_real p_b, const godot_real p_c, const godot_real p_d) {
Plane *dest = (Plane *)r_dest;
*dest = Plane(p_a, p_b, p_c, p_d);
@@ -77,13 +79,6 @@ godot_vector3 GDAPI godot_plane_center(const godot_plane *p_self) {
return dest;
}
-godot_vector3 GDAPI godot_plane_get_any_point(const godot_plane *p_self) {
- godot_vector3 dest;
- const Plane *self = (const Plane *)p_self;
- *((Vector3 *)&dest) = self->get_any_point();
- return dest;
-}
-
godot_bool GDAPI godot_plane_is_point_over(const godot_plane *p_self, const godot_vector3 *p_point) {
const Plane *self = (const Plane *)p_self;
const Vector3 *point = (const Vector3 *)p_point;
diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp
index 15c04f7191..de6308ad2a 100644
--- a/modules/gdnative/gdnative/quat.cpp
+++ b/modules/gdnative/gdnative/quat.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_quat) == sizeof(Quat), "Quat size mismatch");
+
void GDAPI godot_quat_new(godot_quat *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z, const godot_real p_w) {
Quat *dest = (Quat *)r_dest;
*dest = Quat(p_x, p_y, p_z, p_w);
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index a2f735172f..516f4d75ce 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -37,6 +37,11 @@
extern "C" {
#endif
+static_assert(sizeof(godot_rect2) == sizeof(Rect2), "Rect2 size mismatch");
+static_assert(sizeof(godot_rect2i) == sizeof(Rect2i), "Rect2i size mismatch");
+
+// Rect2
+
void GDAPI godot_rect2_new_with_position_and_size(godot_rect2 *r_dest, const godot_vector2 *p_pos, const godot_vector2 *p_size) {
const Vector2 *position = (const Vector2 *)p_pos;
const Vector2 *size = (const Vector2 *)p_size;
@@ -56,6 +61,13 @@ godot_string GDAPI godot_rect2_as_string(const godot_rect2 *p_self) {
return ret;
}
+godot_rect2i GDAPI godot_rect2_as_rect2i(const godot_rect2 *p_self) {
+ godot_rect2i dest;
+ const Rect2 *self = (const Rect2 *)p_self;
+ *((Rect2i *)&dest) = Rect2i(*self);
+ return dest;
+}
+
godot_real GDAPI godot_rect2_get_area(const godot_rect2 *p_self) {
const Rect2 *self = (const Rect2 *)p_self;
return self->get_area();
@@ -171,6 +183,149 @@ void GDAPI godot_rect2_set_size(godot_rect2 *p_self, const godot_vector2 *p_size
self->set_size(*size);
}
+// Rect2i
+
+void GDAPI godot_rect2i_new_with_position_and_size(godot_rect2i *r_dest, const godot_vector2i *p_pos, const godot_vector2i *p_size) {
+ const Vector2i *position = (const Vector2i *)p_pos;
+ const Vector2i *size = (const Vector2i *)p_size;
+ Rect2i *dest = (Rect2i *)r_dest;
+ *dest = Rect2i(*position, *size);
+}
+
+void GDAPI godot_rect2i_new(godot_rect2i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_width, const godot_int p_height) {
+ Rect2i *dest = (Rect2i *)r_dest;
+ *dest = Rect2i(p_x, p_y, p_width, p_height);
+}
+
+godot_string GDAPI godot_rect2i_as_string(const godot_rect2i *p_self) {
+ godot_string ret;
+ const Rect2i *self = (const Rect2i *)p_self;
+ memnew_placement(&ret, String(*self));
+ return ret;
+}
+
+godot_rect2 GDAPI godot_rect2i_as_rect2(const godot_rect2i *p_self) {
+ godot_rect2 dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ *((Rect2 *)&dest) = Rect2(*self);
+ return dest;
+}
+
+godot_int GDAPI godot_rect2i_get_area(const godot_rect2i *p_self) {
+ const Rect2i *self = (const Rect2i *)p_self;
+ return self->get_area();
+}
+
+godot_bool GDAPI godot_rect2i_intersects(const godot_rect2i *p_self, const godot_rect2i *p_b) {
+ const Rect2i *self = (const Rect2i *)p_self;
+ const Rect2i *b = (const Rect2i *)p_b;
+ return self->intersects(*b);
+}
+
+godot_bool GDAPI godot_rect2i_encloses(const godot_rect2i *p_self, const godot_rect2i *p_b) {
+ const Rect2i *self = (const Rect2i *)p_self;
+ const Rect2i *b = (const Rect2i *)p_b;
+ return self->encloses(*b);
+}
+
+godot_bool GDAPI godot_rect2i_has_no_area(const godot_rect2i *p_self) {
+ const Rect2i *self = (const Rect2i *)p_self;
+ return self->has_no_area();
+}
+
+godot_rect2i GDAPI godot_rect2i_clip(const godot_rect2i *p_self, const godot_rect2i *p_b) {
+ godot_rect2i dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ const Rect2i *b = (const Rect2i *)p_b;
+ *((Rect2i *)&dest) = self->clip(*b);
+ return dest;
+}
+
+godot_rect2i GDAPI godot_rect2i_merge(const godot_rect2i *p_self, const godot_rect2i *p_b) {
+ godot_rect2i dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ const Rect2i *b = (const Rect2i *)p_b;
+ *((Rect2i *)&dest) = self->merge(*b);
+ return dest;
+}
+
+godot_bool GDAPI godot_rect2i_has_point(const godot_rect2i *p_self, const godot_vector2i *p_point) {
+ const Rect2i *self = (const Rect2i *)p_self;
+ const Vector2i *point = (const Vector2i *)p_point;
+ return self->has_point(*point);
+}
+
+godot_rect2i GDAPI godot_rect2i_grow(const godot_rect2i *p_self, const godot_int p_by) {
+ godot_rect2i dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+
+ *((Rect2i *)&dest) = self->grow(p_by);
+ return dest;
+}
+
+godot_rect2i GDAPI godot_rect2i_grow_individual(const godot_rect2i *p_self, const godot_int p_left, const godot_int p_top, const godot_int p_right, const godot_int p_bottom) {
+ godot_rect2i dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ *((Rect2i *)&dest) = self->grow_individual(p_left, p_top, p_right, p_bottom);
+ return dest;
+}
+
+godot_rect2i GDAPI godot_rect2i_grow_margin(const godot_rect2i *p_self, const godot_int p_margin, const godot_int p_by) {
+ godot_rect2i dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ *((Rect2i *)&dest) = self->grow_margin((Margin)p_margin, p_by);
+ return dest;
+}
+
+godot_rect2i GDAPI godot_rect2i_abs(const godot_rect2i *p_self) {
+ godot_rect2i dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ *((Rect2i *)&dest) = self->abs();
+ return dest;
+}
+
+godot_rect2i GDAPI godot_rect2i_expand(const godot_rect2i *p_self, const godot_vector2i *p_to) {
+ godot_rect2i dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ const Vector2i *to = (const Vector2i *)p_to;
+ *((Rect2i *)&dest) = self->expand(*to);
+ return dest;
+}
+
+godot_bool GDAPI godot_rect2i_operator_equal(const godot_rect2i *p_self, const godot_rect2i *p_b) {
+ const Rect2i *self = (const Rect2i *)p_self;
+ const Rect2i *b = (const Rect2i *)p_b;
+ return *self == *b;
+}
+
+godot_vector2i GDAPI godot_rect2i_get_position(const godot_rect2i *p_self) {
+ godot_vector2i dest;
+ Vector2i *d = (Vector2i *)&dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ *d = self->get_position();
+ return dest;
+}
+
+godot_vector2i GDAPI godot_rect2i_get_size(const godot_rect2i *p_self) {
+ godot_vector2i dest;
+ Vector2i *d = (Vector2i *)&dest;
+ const Rect2i *self = (const Rect2i *)p_self;
+ *d = self->get_size();
+ return dest;
+}
+
+void GDAPI godot_rect2i_set_position(godot_rect2i *p_self, const godot_vector2i *p_pos) {
+ Rect2i *self = (Rect2i *)p_self;
+ const Vector2i *position = (const Vector2i *)p_pos;
+ self->set_position(*position);
+}
+
+void GDAPI godot_rect2i_set_size(godot_rect2i *p_self, const godot_vector2i *p_size) {
+ Rect2i *self = (Rect2i *)p_self;
+ const Vector2i *size = (const Vector2i *)p_size;
+ self->set_size(*size);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/rid.cpp b/modules/gdnative/gdnative/rid.cpp
index 7ea80123a3..d7a63f33a7 100644
--- a/modules/gdnative/gdnative/rid.cpp
+++ b/modules/gdnative/gdnative/rid.cpp
@@ -38,6 +38,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_rid) == sizeof(RID), "RID size mismatch");
+
void GDAPI godot_rid_new(godot_rid *r_dest) {
RID *dest = (RID *)r_dest;
memnew_placement(dest, RID);
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index a22af89edc..1fa19f4ff5 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -40,6 +40,11 @@
extern "C" {
#endif
+static_assert(sizeof(godot_char16_string) == sizeof(Char16String), "Char16String size mismatch");
+static_assert(sizeof(godot_char_string) == sizeof(CharString), "CharString size mismatch");
+static_assert(sizeof(godot_string) == sizeof(String), "String size mismatch");
+static_assert(sizeof(godot_char_type) == sizeof(char32_t), "char32_t size mismatch");
+
godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs) {
const CharString *cs = (const CharString *)p_cs;
@@ -58,6 +63,24 @@ void GDAPI godot_char_string_destroy(godot_char_string *p_cs) {
cs->~CharString();
}
+godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs) {
+ const Char16String *cs = (const Char16String *)p_cs;
+
+ return cs->length();
+}
+
+const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs) {
+ const Char16String *cs = (const Char16String *)p_cs;
+
+ return cs->get_data();
+}
+
+void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs) {
+ Char16String *cs = (Char16String *)p_cs;
+
+ cs->~Char16String();
+}
+
void GDAPI godot_string_new(godot_string *r_dest) {
String *dest = (String *)r_dest;
memnew_placement(dest, String);
@@ -66,27 +89,97 @@ void GDAPI godot_string_new(godot_string *r_dest) {
void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src) {
String *dest = (String *)r_dest;
const String *src = (const String *)p_src;
- memnew_placement(dest, String(*src));
+ memnew_placement(dest, String);
+ *dest = String(*src);
}
-void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size) {
+void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents) {
String *dest = (String *)r_dest;
- memnew_placement(dest, String(p_contents, p_size));
+ memnew_placement(dest, String);
+ *dest = String(p_contents);
}
-const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) {
+void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf8(p_contents);
+}
+
+void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf16(p_contents);
+}
+
+void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents);
+}
+
+void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents) {
+ String *dest = (String *)r_dest;
+ if (sizeof(wchar_t) == 2) {
+ // wchar_t is 16 bit, parse.
+ memnew_placement(dest, String);
+ dest->parse_utf16((const char16_t *)p_contents);
+ } else {
+ // wchar_t is 32 bit, copy.
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents);
+ }
+}
+
+void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String(p_contents, p_size);
+}
+
+void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf8(p_contents, p_size);
+}
+
+void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf16(p_contents, p_size);
+}
+
+void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents, p_size);
+}
+
+void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ if (sizeof(wchar_t) == 2) {
+ // wchar_t is 16 bit, parse.
+ memnew_placement(dest, String);
+ dest->parse_utf16((const char16_t *)p_contents, p_size);
+ } else {
+ // wchar_t is 32 bit, copy.
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents, p_size);
+ }
+}
+
+const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) {
String *self = (String *)p_self;
return &(self->operator[](p_idx));
}
-wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) {
+godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) {
const String *self = (const String *)p_self;
return self->operator[](p_idx);
}
-const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self) {
+const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self) {
const String *self = (const String *)p_self;
- return self->c_str();
+ return self->get_data();
}
godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b) {
@@ -158,22 +251,14 @@ godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self,
return self->begins_with(p_char_array);
}
-godot_array GDAPI godot_string_bigrams(const godot_string *p_self) {
+godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self) {
const String *self = (const String *)p_self;
- Vector<String> return_value = self->bigrams();
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
-
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->bigrams()));
+ return ret;
};
-godot_string GDAPI godot_string_chr(wchar_t p_character) {
+godot_string GDAPI godot_string_chr(godot_char_type p_character) {
godot_string result;
memnew_placement(&result, String(String::chr(p_character)));
@@ -187,99 +272,77 @@ godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_
return self->ends_with(*string);
}
-godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) {
+godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+
+ return self->ends_with(p_char_array);
+}
+
+godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) {
+ const String *self = (const String *)p_self;
+ const String *what = (const String *)p_what;
return self->count(*what, p_from, p_to);
}
-godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) {
+godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->countn(*what, p_from, p_to);
}
-godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->find(*what);
}
-godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->find(*what, p_from);
}
-godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys) {
+godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys) {
const String *self = (const String *)p_self;
-
- Vector<String> keys;
- Array *keys_proxy = (Array *)p_keys;
- keys.resize(keys_proxy->size());
- for (int i = 0; i < keys_proxy->size(); i++) {
- keys.write[i] = (*keys_proxy)[i];
- }
-
- return self->findmk(keys);
+ const Vector<String> *keys = (const Vector<String> *)p_keys;
+ return self->findmk(*keys);
}
-godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from) {
+godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from) {
const String *self = (const String *)p_self;
-
- Vector<String> keys;
- Array *keys_proxy = (Array *)p_keys;
- keys.resize(keys_proxy->size());
- for (int i = 0; i < keys_proxy->size(); i++) {
- keys.write[i] = (*keys_proxy)[i];
- }
-
- return self->findmk(keys, p_from);
+ const Vector<String> *keys = (const Vector<String> *)p_keys;
+ return self->findmk(*keys, p_from);
}
-godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key) {
+godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key) {
const String *self = (const String *)p_self;
-
- Vector<String> keys;
- Array *keys_proxy = (Array *)p_keys;
- keys.resize(keys_proxy->size());
- for (int i = 0; i < keys_proxy->size(); i++) {
- keys.write[i] = (*keys_proxy)[i];
- }
-
+ const Vector<String> *keys = (const Vector<String> *)p_keys;
int key;
- int ret = self->findmk(keys, p_from, &key);
+ int ret = self->findmk(*keys, p_from, &key);
if (r_key) {
*r_key = key;
}
return ret;
}
-godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->findn(*what);
}
-godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->findn(*what, p_from);
}
-godot_int GDAPI godot_string_find_last(const godot_string *p_self, godot_string p_what) {
- const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
-
- return self->find_last(*what);
-}
-
godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values) {
const String *self = (const String *)p_self;
const Variant *values = (const Variant *)p_values;
@@ -306,21 +369,9 @@ godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot
return result;
}
-godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hex_to_int();
-}
-
-godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hex_to_int(true);
-}
-
-godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string) {
+godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string) {
const String *self = (const String *)p_self;
- String *content = (String *)&p_string;
+ const String *content = (const String *)p_string;
godot_string result;
memnew_placement(&result, String(self->insert(p_at_pos, *content)));
@@ -443,58 +494,58 @@ godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int
return result;
}
-godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with) {
+godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
const String *self = (const String *)p_self;
- String *key = (String *)&p_key;
- String *with = (String *)&p_with;
+ const String *key = (const String *)p_key;
+ const String *with = (const String *)p_with;
godot_string result;
memnew_placement(&result, String(self->replace(*key, *with)));
return result;
}
-godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with) {
+godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
const String *self = (const String *)p_self;
- String *key = (String *)&p_key;
- String *with = (String *)&p_with;
+ const String *key = (const String *)p_key;
+ const String *with = (const String *)p_with;
godot_string result;
memnew_placement(&result, String(self->replacen(*key, *with)));
return result;
}
-godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfind(*what);
}
-godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfindn(*what);
}
-godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfind(*what, p_from);
}
-godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfindn(*what, p_from);
}
-godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with) {
+godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
const String *self = (const String *)p_self;
- String *key = (String *)&p_key;
- String *with = (String *)&p_with;
+ const String *key = (const String *)p_key;
+ const String *with = (const String *)p_with;
godot_string result;
memnew_placement(&result, String(self->replace_first(*key, *with)));
@@ -544,24 +595,18 @@ godot_string GDAPI godot_string_substr(const godot_string *p_self, godot_int p_f
return result;
}
-double GDAPI godot_string_to_double(const godot_string *p_self) {
+godot_int GDAPI godot_string_to_int(const godot_string *p_self) {
const String *self = (const String *)p_self;
- return self->to_double();
+ return self->to_int();
}
-godot_real GDAPI godot_string_to_float(const godot_string *p_self) {
+double GDAPI godot_string_to_float(const godot_string *p_self) {
const String *self = (const String *)p_self;
return self->to_float();
}
-godot_int GDAPI godot_string_to_int(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->to_int();
-}
-
godot_string GDAPI godot_string_capitalize(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_string result;
@@ -586,15 +631,19 @@ godot_string GDAPI godot_string_camelcase_to_underscore_lowercased(const godot_s
return result;
}
-double GDAPI godot_string_char_to_double(const char *p_what) {
- return String::to_double(p_what);
+double GDAPI godot_string_char_to_float(const char *p_what) {
+ return String::to_float(p_what);
+}
+
+double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end) {
+ return String::to_float(p_str, r_end);
}
godot_int GDAPI godot_string_char_to_int(const char *p_what) {
return String::to_int(p_what);
}
-int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str) {
+godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str) {
return String::to_int(p_str);
}
@@ -602,42 +651,32 @@ godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int
return String::to_int(p_what, p_len);
}
-int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len) {
+godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len) {
return String::to_int(p_str, p_len);
}
-int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hex_to_int64(false);
-}
-
-int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self) {
+godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) {
const String *self = (const String *)p_self;
- return self->hex_to_int64();
+ return self->hex_to_int(false);
}
-int64_t GDAPI godot_string_to_int64(const godot_string *p_self) {
+godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self) {
const String *self = (const String *)p_self;
- return self->to_int64();
-}
-
-double GDAPI godot_string_unicode_char_to_double(const wchar_t *p_str, const wchar_t **r_end) {
- return String::to_double(p_str, r_end);
+ return self->hex_to_int();
}
-godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice) {
+godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice) {
const String *self = (const String *)p_self;
- String *splitter = (String *)&p_splitter;
+ const String *splitter = (const String *)p_splitter;
godot_string result;
memnew_placement(&result, String(self->get_slice(*splitter, p_slice)));
return result;
}
-godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice) {
+godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice) {
const String *self = (const String *)p_self;
godot_string result;
memnew_placement(&result, String(self->get_slicec(p_splitter, p_slice)));
@@ -645,221 +684,149 @@ godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p
return result;
}
-godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<String> return_value = self->split(*splitter, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
-
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<String> return_value = self->split(*splitter);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split(*splitter, true)));
+ return ret;
+}
- return result;
+godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) {
+ const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split(*splitter, p_allow_empty, p_maxsplit)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats(*splitter, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats(*splitter);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) {
const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats_mk(splitters, false);
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, p_allow_empty, p_maxsplit)));
+ return ret;
+}
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) {
+ const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- return result;
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats_mk(splitters);
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, true)));
+ return ret;
+}
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
+ const String *self = (const String *)p_self;
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- return result;
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints(*splitter, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- return result;
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints(*splitter);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
- return result;
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints_mk(splitters, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
-
- return result;
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
const String *self = (const String *)p_self;
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints_mk(splitters);
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, false)));
+ return ret;
+}
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
+ const String *self = (const String *)p_self;
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- return result;
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_spaces(const godot_string *p_self) {
+godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self) {
const String *self = (const String *)p_self;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<String> return_value = self->split_spaces();
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split_spaces()));
+ return ret;
}
-godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter) {
+godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
- String *splitter = (String *)&p_splitter;
+ const String *splitter = (const String *)p_splitter;
return self->get_slice_count(*splitter);
}
-wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char) {
+godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char) {
return String::char_lowercase(p_char);
}
-wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char) {
+godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char) {
return String::char_uppercase(p_char);
}
@@ -903,7 +870,7 @@ godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos
return result;
}
-wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) {
+godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) {
const String *self = (const String *)p_self;
return self->ord_at(p_idx);
@@ -926,6 +893,14 @@ godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_po
return result;
}
+godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ memnew_placement(&result, String(self->repeat(p_count)));
+
+ return result;
+}
+
godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right) {
const String *self = (const String *)p_self;
godot_string result;
@@ -957,7 +932,7 @@ godot_char_string GDAPI godot_string_ascii(const godot_string *p_self) {
return result;
}
-godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self) {
+godot_char_string GDAPI godot_string_latin1(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_char_string result;
@@ -1003,6 +978,42 @@ godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot
return result;
}
+godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+
+ godot_char16_string result;
+
+ memnew_placement(&result, Char16String(self->utf16()));
+
+ return result;
+}
+
+godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16) {
+ String *self = (String *)p_self;
+
+ return self->parse_utf16(p_utf16);
+}
+
+godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len) {
+ String *self = (String *)p_self;
+
+ return self->parse_utf16(p_utf16, p_len);
+}
+
+godot_string GDAPI godot_string_chars_to_utf16(const char16_t *p_utf16) {
+ godot_string result;
+ memnew_placement(&result, String(String::utf16(p_utf16)));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_chars_to_utf16_with_len(const char16_t *p_utf16, godot_int p_len) {
+ godot_string result;
+ memnew_placement(&result, String(String::utf16(p_utf16, p_len)));
+
+ return result;
+}
+
uint32_t GDAPI godot_string_hash(const godot_string *p_self) {
const String *self = (const String *)p_self;
@@ -1023,28 +1034,18 @@ uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_
return String::hash(p_cstr, p_len);
}
-uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str) {
+uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str) {
return String::hash(p_str);
}
-uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len) {
+uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len) {
return String::hash(p_str, p_len);
}
godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self) {
const String *self = (const String *)p_self;
- Vector<uint8_t> tmp_result = self->md5_buffer();
-
godot_packed_byte_array result;
- memnew_placement(&result, PackedByteArray);
- PackedByteArray *proxy = (PackedByteArray *)&result;
- uint8_t *proxy_writer = proxy->ptrw();
- proxy->resize(tmp_result.size());
-
- for (int i = 0; i < tmp_result.size(); i++) {
- proxy_writer[i] = tmp_result[i];
- }
-
+ memnew_placement(&result, PackedByteArray(self->md5_buffer()));
return result;
}
@@ -1056,23 +1057,28 @@ godot_string GDAPI godot_string_md5_text(const godot_string *p_self) {
return result;
}
-godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) {
+godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self) {
const String *self = (const String *)p_self;
- Vector<uint8_t> tmp_result = self->sha256_buffer();
-
godot_packed_byte_array result;
- memnew_placement(&result, PackedByteArray);
- PackedByteArray *proxy = (PackedByteArray *)&result;
- uint8_t *proxy_writer = proxy->ptrw();
- proxy->resize(tmp_result.size());
+ memnew_placement(&result, PackedByteArray(self->sha1_buffer()));
+ return result;
+}
- for (int i = 0; i < tmp_result.size(); i++) {
- proxy_writer[i] = tmp_result[i];
- }
+godot_string GDAPI godot_string_sha1_text(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ memnew_placement(&result, String(self->sha1_text()));
return result;
}
+godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_packed_byte_array result;
+ memnew_placement(&result, PackedByteArray(self->sha256_buffer()));
+ return result;
+}
+
godot_string GDAPI godot_string_sha256_text(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_string result;
@@ -1215,15 +1221,6 @@ godot_string GDAPI godot_string_json_escape(const godot_string *p_self) {
return result;
}
-godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->word_wrap(p_chars_per_line);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
godot_string GDAPI godot_string_xml_escape(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_string result;
@@ -1269,6 +1266,22 @@ godot_string GDAPI godot_string_percent_encode(const godot_string *p_self) {
return result;
}
+godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts) {
+ const String *self = (const String *)p_self;
+ const Vector<String> *parts = (const Vector<String> *)p_parts;
+ godot_string result;
+ String return_value = self->join(*parts);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+
+ return self->is_valid_filename();
+}
+
godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self) {
const String *self = (const String *)p_self;
@@ -1334,31 +1347,22 @@ godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const go
return result;
}
-godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) {
+godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars) {
const String *self = (const String *)p_self;
String *chars = (String *)p_chars;
godot_string result;
- String return_value = self->rstrip(*chars);
+ String return_value = self->lstrip(*chars);
memnew_placement(&result, String(return_value));
return result;
}
-godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor,
- const godot_bool p_allow_empty, const godot_int p_maxsplit) {
+godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) {
const String *self = (const String *)p_self;
- String *divisor = (String *)p_divisor;
-
- godot_packed_string_array result;
- memnew_placement(&result, PackedStringArray);
- PackedStringArray *proxy = (PackedStringArray *)&result;
- String *proxy_writer = proxy->ptrw();
- Vector<String> tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit);
- proxy->resize(tmp_result.size());
-
- for (int i = 0; i < tmp_result.size(); i++) {
- proxy_writer[i] = tmp_result[i];
- }
+ String *chars = (String *)p_chars;
+ godot_string result;
+ String return_value = self->rstrip(*chars);
+ memnew_placement(&result, String(return_value));
return result;
}
diff --git a/modules/gdnative/gdnative/string_name.cpp b/modules/gdnative/gdnative/string_name.cpp
index 1abb4486b1..7bbaaeeaa0 100644
--- a/modules/gdnative/gdnative/string_name.cpp
+++ b/modules/gdnative/gdnative/string_name.cpp
@@ -39,6 +39,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_string_name) == sizeof(StringName), "StringName size mismatch");
+
void GDAPI godot_string_name_new(godot_string_name *r_dest, const godot_string *p_name) {
StringName *dest = (StringName *)r_dest;
const String *name = (const String *)p_name;
diff --git a/modules/gdnative/gdnative/transform.cpp b/modules/gdnative/gdnative/transform.cpp
index c9b3e37fb2..d19de93e9b 100644
--- a/modules/gdnative/gdnative/transform.cpp
+++ b/modules/gdnative/gdnative/transform.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_transform) == sizeof(Transform), "Transform size mismatch");
+
void GDAPI godot_transform_new_with_axis_origin(godot_transform *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis, const godot_vector3 *p_origin) {
const Vector3 *x_axis = (const Vector3 *)p_x_axis;
const Vector3 *y_axis = (const Vector3 *)p_y_axis;
diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp
index 26a71333b1..c0f7878eb0 100644
--- a/modules/gdnative/gdnative/transform2d.cpp
+++ b/modules/gdnative/gdnative/transform2d.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_transform2d) == sizeof(Transform2D), "Transform2D size mismatch");
+
void GDAPI godot_transform2d_new(godot_transform2d *r_dest, const godot_real p_rot, const godot_vector2 *p_pos) {
const Vector2 *pos = (const Vector2 *)p_pos;
Transform2D *dest = (Transform2D *)r_dest;
diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp
index f0fc44ae8a..dac4feb0e5 100644
--- a/modules/gdnative/gdnative/variant.cpp
+++ b/modules/gdnative/gdnative/variant.cpp
@@ -37,6 +37,8 @@
extern "C" {
#endif
+static_assert(sizeof(godot_variant) == sizeof(Variant), "Variant size mismatch");
+
// Workaround GCC ICE on armv7hl which was affected GCC 6.0 up to 8.0 (GH-16100).
// It was fixed upstream in 8.1, and a fix was backported to 7.4.
// This can be removed once no supported distro ships with versions older than 7.4.
@@ -97,24 +99,48 @@ void GDAPI godot_variant_new_string(godot_variant *r_dest, const godot_string *p
memnew_placement_custom(dest, Variant, Variant(*s));
}
+void GDAPI godot_variant_new_string_name(godot_variant *r_dest, const godot_string_name *p_s) {
+ Variant *dest = (Variant *)r_dest;
+ StringName *s = (StringName *)p_s;
+ memnew_placement_custom(dest, Variant, Variant(*s));
+}
+
void GDAPI godot_variant_new_vector2(godot_variant *r_dest, const godot_vector2 *p_v2) {
Variant *dest = (Variant *)r_dest;
Vector2 *v2 = (Vector2 *)p_v2;
memnew_placement_custom(dest, Variant, Variant(*v2));
}
+void GDAPI godot_variant_new_vector2i(godot_variant *r_dest, const godot_vector2i *p_v2) {
+ Variant *dest = (Variant *)r_dest;
+ Vector2i *v2 = (Vector2i *)p_v2;
+ memnew_placement_custom(dest, Variant, Variant(*v2));
+}
+
void GDAPI godot_variant_new_rect2(godot_variant *r_dest, const godot_rect2 *p_rect2) {
Variant *dest = (Variant *)r_dest;
Rect2 *rect2 = (Rect2 *)p_rect2;
memnew_placement_custom(dest, Variant, Variant(*rect2));
}
+void GDAPI godot_variant_new_rect2i(godot_variant *r_dest, const godot_rect2i *p_rect2) {
+ Variant *dest = (Variant *)r_dest;
+ Rect2i *rect2 = (Rect2i *)p_rect2;
+ memnew_placement_custom(dest, Variant, Variant(*rect2));
+}
+
void GDAPI godot_variant_new_vector3(godot_variant *r_dest, const godot_vector3 *p_v3) {
Variant *dest = (Variant *)r_dest;
Vector3 *v3 = (Vector3 *)p_v3;
memnew_placement_custom(dest, Variant, Variant(*v3));
}
+void GDAPI godot_variant_new_vector3i(godot_variant *r_dest, const godot_vector3i *p_v3) {
+ Variant *dest = (Variant *)r_dest;
+ Vector3i *v3 = (Vector3i *)p_v3;
+ memnew_placement_custom(dest, Variant, Variant(*v3));
+}
+
void GDAPI godot_variant_new_transform2d(godot_variant *r_dest, const godot_transform2d *p_t2d) {
Variant *dest = (Variant *)r_dest;
Transform2D *t2d = (Transform2D *)p_t2d;
@@ -169,6 +195,18 @@ void GDAPI godot_variant_new_rid(godot_variant *r_dest, const godot_rid *p_rid)
memnew_placement_custom(dest, Variant, Variant(*rid));
}
+void GDAPI godot_variant_new_callable(godot_variant *r_dest, const godot_callable *p_cb) {
+ Variant *dest = (Variant *)r_dest;
+ Callable *cb = (Callable *)p_cb;
+ memnew_placement_custom(dest, Variant, Variant(*cb));
+}
+
+void GDAPI godot_variant_new_signal(godot_variant *r_dest, const godot_signal *p_signal) {
+ Variant *dest = (Variant *)r_dest;
+ Signal *signal = (Signal *)p_signal;
+ memnew_placement_custom(dest, Variant, Variant(*signal));
+}
+
void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p_obj) {
Variant *dest = (Variant *)r_dest;
Object *obj = (Object *)p_obj;
@@ -207,18 +245,30 @@ void GDAPI godot_variant_new_packed_byte_array(godot_variant *r_dest, const godo
memnew_placement_custom(dest, Variant, Variant(*pba));
}
-void GDAPI godot_variant_new_packed_int_array(godot_variant *r_dest, const godot_packed_int_array *p_pia) {
+void GDAPI godot_variant_new_packed_int32_array(godot_variant *r_dest, const godot_packed_int32_array *p_pia) {
Variant *dest = (Variant *)r_dest;
PackedInt32Array *pia = (PackedInt32Array *)p_pia;
memnew_placement_custom(dest, Variant, Variant(*pia));
}
-void GDAPI godot_variant_new_packed_real_array(godot_variant *r_dest, const godot_packed_real_array *p_pra) {
+void GDAPI godot_variant_new_packed_int64_array(godot_variant *r_dest, const godot_packed_int64_array *p_pia) {
+ Variant *dest = (Variant *)r_dest;
+ PackedInt64Array *pia = (PackedInt64Array *)p_pia;
+ memnew_placement_custom(dest, Variant, Variant(*pia));
+}
+
+void GDAPI godot_variant_new_packed_float32_array(godot_variant *r_dest, const godot_packed_float32_array *p_pra) {
Variant *dest = (Variant *)r_dest;
PackedFloat32Array *pra = (PackedFloat32Array *)p_pra;
memnew_placement_custom(dest, Variant, Variant(*pra));
}
+void GDAPI godot_variant_new_packed_float64_array(godot_variant *r_dest, const godot_packed_float64_array *p_pra) {
+ Variant *dest = (Variant *)r_dest;
+ PackedFloat64Array *pra = (PackedFloat64Array *)p_pra;
+ memnew_placement_custom(dest, Variant, Variant(*pra));
+}
+
void GDAPI godot_variant_new_packed_string_array(godot_variant *r_dest, const godot_packed_string_array *p_psa) {
Variant *dest = (Variant *)r_dest;
PackedStringArray *psa = (PackedStringArray *)p_psa;
@@ -271,6 +321,14 @@ godot_string GDAPI godot_variant_as_string(const godot_variant *p_self) {
return raw_dest;
}
+godot_string_name GDAPI godot_variant_as_string_name(const godot_variant *p_self) {
+ godot_string_name raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ StringName *dest = (StringName *)&raw_dest;
+ memnew_placement(dest, StringName(self->operator StringName())); // operator = is overloaded by StringName
+ return raw_dest;
+}
+
godot_vector2 GDAPI godot_variant_as_vector2(const godot_variant *p_self) {
godot_vector2 raw_dest;
const Variant *self = (const Variant *)p_self;
@@ -279,6 +337,14 @@ godot_vector2 GDAPI godot_variant_as_vector2(const godot_variant *p_self) {
return raw_dest;
}
+godot_vector2i GDAPI godot_variant_as_vector2i(const godot_variant *p_self) {
+ godot_vector2i raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ *dest = *self;
+ return raw_dest;
+}
+
godot_rect2 GDAPI godot_variant_as_rect2(const godot_variant *p_self) {
godot_rect2 raw_dest;
const Variant *self = (const Variant *)p_self;
@@ -287,6 +353,14 @@ godot_rect2 GDAPI godot_variant_as_rect2(const godot_variant *p_self) {
return raw_dest;
}
+godot_rect2i GDAPI godot_variant_as_rect2i(const godot_variant *p_self) {
+ godot_rect2i raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ Rect2i *dest = (Rect2i *)&raw_dest;
+ *dest = *self;
+ return raw_dest;
+}
+
godot_vector3 GDAPI godot_variant_as_vector3(const godot_variant *p_self) {
godot_vector3 raw_dest;
const Variant *self = (const Variant *)p_self;
@@ -295,6 +369,14 @@ godot_vector3 GDAPI godot_variant_as_vector3(const godot_variant *p_self) {
return raw_dest;
}
+godot_vector3i GDAPI godot_variant_as_vector3i(const godot_variant *p_self) {
+ godot_vector3i raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ *dest = *self;
+ return raw_dest;
+}
+
godot_transform2d GDAPI godot_variant_as_transform2d(const godot_variant *p_self) {
godot_transform2d raw_dest;
const Variant *self = (const Variant *)p_self;
@@ -367,6 +449,22 @@ godot_rid GDAPI godot_variant_as_rid(const godot_variant *p_self) {
return raw_dest;
}
+godot_callable GDAPI godot_variant_as_callable(const godot_variant *p_self) {
+ godot_callable raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ Callable *dest = (Callable *)&raw_dest;
+ *dest = *self;
+ return raw_dest;
+}
+
+godot_signal GDAPI godot_variant_as_signal(const godot_variant *p_self) {
+ godot_signal raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ Signal *dest = (Signal *)&raw_dest;
+ *dest = *self;
+ return raw_dest;
+}
+
godot_object GDAPI *godot_variant_as_object(const godot_variant *p_self) {
const Variant *self = (const Variant *)p_self;
Object *dest;
@@ -399,8 +497,8 @@ godot_packed_byte_array GDAPI godot_variant_as_packed_byte_array(const godot_var
return raw_dest;
}
-godot_packed_int_array GDAPI godot_variant_as_packed_int_array(const godot_variant *p_self) {
- godot_packed_int_array raw_dest;
+godot_packed_int32_array GDAPI godot_variant_as_packed_int32_array(const godot_variant *p_self) {
+ godot_packed_int32_array raw_dest;
const Variant *self = (const Variant *)p_self;
PackedInt32Array *dest = (PackedInt32Array *)&raw_dest;
memnew_placement(dest, PackedInt32Array(self->operator PackedInt32Array())); // operator = is overloaded by PackedInt32Array
@@ -408,8 +506,17 @@ godot_packed_int_array GDAPI godot_variant_as_packed_int_array(const godot_varia
return raw_dest;
}
-godot_packed_real_array GDAPI godot_variant_as_packed_real_array(const godot_variant *p_self) {
- godot_packed_real_array raw_dest;
+godot_packed_int64_array GDAPI godot_variant_as_packed_int64_array(const godot_variant *p_self) {
+ godot_packed_int64_array raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ PackedInt64Array *dest = (PackedInt64Array *)&raw_dest;
+ memnew_placement(dest, PackedInt64Array(self->operator PackedInt64Array())); // operator = is overloaded by PackedInt64Array
+ *dest = *self;
+ return raw_dest;
+}
+
+godot_packed_float32_array GDAPI godot_variant_as_packed_float32_array(const godot_variant *p_self) {
+ godot_packed_float32_array raw_dest;
const Variant *self = (const Variant *)p_self;
PackedFloat32Array *dest = (PackedFloat32Array *)&raw_dest;
memnew_placement(dest, PackedFloat32Array(self->operator PackedFloat32Array())); // operator = is overloaded by PackedFloat32Array
@@ -417,6 +524,15 @@ godot_packed_real_array GDAPI godot_variant_as_packed_real_array(const godot_var
return raw_dest;
}
+godot_packed_float64_array GDAPI godot_variant_as_packed_float64_array(const godot_variant *p_self) {
+ godot_packed_float64_array raw_dest;
+ const Variant *self = (const Variant *)p_self;
+ PackedFloat64Array *dest = (PackedFloat64Array *)&raw_dest;
+ memnew_placement(dest, PackedFloat64Array(self->operator PackedFloat64Array())); // operator = is overloaded by PackedFloat64Array
+ *dest = *self;
+ return raw_dest;
+}
+
godot_packed_string_array GDAPI godot_variant_as_packed_string_array(const godot_variant *p_self) {
godot_packed_string_array raw_dest;
const Variant *self = (const Variant *)p_self;
@@ -487,6 +603,11 @@ godot_bool GDAPI godot_variant_operator_less(const godot_variant *p_self, const
return self->operator<(*other);
}
+uint32_t GDAPI godot_variant_hash(const godot_variant *p_self) {
+ const Variant *self = (const Variant *)p_self;
+ return self->hash();
+}
+
godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const godot_variant *p_other) {
const Variant *self = (const Variant *)p_self;
const Variant *other = (const Variant *)p_other;
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index b6c3569f42..1ee716df86 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -37,6 +37,11 @@
extern "C" {
#endif
+static_assert(sizeof(godot_vector2) == sizeof(Vector2), "Vector2 size mismatch");
+static_assert(sizeof(godot_vector2i) == sizeof(Vector2i), "Vector2i size mismatch");
+
+// Vector2
+
void GDAPI godot_vector2_new(godot_vector2 *r_dest, const godot_real p_x, const godot_real p_y) {
Vector2 *dest = (Vector2 *)r_dest;
*dest = Vector2(p_x, p_y);
@@ -49,6 +54,13 @@ godot_string GDAPI godot_vector2_as_string(const godot_vector2 *p_self) {
return ret;
}
+godot_vector2i GDAPI godot_vector2_as_vector2i(const godot_vector2 *p_self) {
+ godot_vector2i dest;
+ const Vector2 *self = (const Vector2 *)p_self;
+ *((Vector2i *)&dest) = Vector2i(*self);
+ return dest;
+}
+
godot_vector2 GDAPI godot_vector2_normalized(const godot_vector2 *p_self) {
godot_vector2 dest;
const Vector2 *self = (const Vector2 *)p_self;
@@ -156,6 +168,13 @@ godot_vector2 GDAPI godot_vector2_floor(const godot_vector2 *p_self) {
return dest;
}
+godot_vector2 GDAPI godot_vector2_sign(const godot_vector2 *p_self) {
+ godot_vector2 dest;
+ const Vector2 *self = (const Vector2 *)p_self;
+ *((Vector2 *)&dest) = self->sign();
+ return dest;
+}
+
godot_vector2 GDAPI godot_vector2_snapped(const godot_vector2 *p_self, const godot_vector2 *p_by) {
godot_vector2 dest;
const Vector2 *self = (const Vector2 *)p_self;
@@ -306,6 +325,138 @@ godot_real GDAPI godot_vector2_get_y(const godot_vector2 *p_self) {
return self->y;
}
+// Vector2i
+
+void GDAPI godot_vector2i_new(godot_vector2i *r_dest, const godot_int p_x, const godot_int p_y) {
+ Vector2i *dest = (Vector2i *)r_dest;
+ *dest = Vector2i(p_x, p_y);
+}
+
+godot_string GDAPI godot_vector2i_as_string(const godot_vector2i *p_self) {
+ godot_string ret;
+ const Vector2i *self = (const Vector2i *)p_self;
+ memnew_placement(&ret, String(*self));
+ return ret;
+}
+
+godot_vector2 GDAPI godot_vector2i_as_vector2(const godot_vector2i *p_self) {
+ godot_vector2 dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ *((Vector2 *)&dest) = Vector2(*self);
+ return dest;
+}
+
+godot_real GDAPI godot_vector2i_aspect(const godot_vector2i *p_self) {
+ const Vector2i *self = (const Vector2i *)p_self;
+ return self->aspect();
+}
+
+godot_vector2i GDAPI godot_vector2i_abs(const godot_vector2i *p_self) {
+ godot_vector2i dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ *((Vector2i *)&dest) = self->abs();
+ return dest;
+}
+
+godot_vector2i GDAPI godot_vector2i_sign(const godot_vector2i *p_self) {
+ godot_vector2i dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ *((Vector2i *)&dest) = self->sign();
+ return dest;
+}
+
+godot_vector2i GDAPI godot_vector2i_operator_add(const godot_vector2i *p_self, const godot_vector2i *p_b) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ const Vector2i *b = (const Vector2i *)p_b;
+ *dest = *self + *b;
+ return raw_dest;
+}
+
+godot_vector2i GDAPI godot_vector2i_operator_subtract(const godot_vector2i *p_self, const godot_vector2i *p_b) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ const Vector2i *b = (const Vector2i *)p_b;
+ *dest = *self - *b;
+ return raw_dest;
+}
+
+godot_vector2i GDAPI godot_vector2i_operator_multiply_vector(const godot_vector2i *p_self, const godot_vector2i *p_b) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ const Vector2i *b = (const Vector2i *)p_b;
+ *dest = *self * *b;
+ return raw_dest;
+}
+
+godot_vector2i GDAPI godot_vector2i_operator_multiply_scalar(const godot_vector2i *p_self, const godot_int p_b) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ *dest = *self * p_b;
+ return raw_dest;
+}
+
+godot_vector2i GDAPI godot_vector2i_operator_divide_vector(const godot_vector2i *p_self, const godot_vector2i *p_b) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ const Vector2i *b = (const Vector2i *)p_b;
+ *dest = *self / *b;
+ return raw_dest;
+}
+
+godot_vector2i GDAPI godot_vector2i_operator_divide_scalar(const godot_vector2i *p_self, const godot_int p_b) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ *dest = *self / p_b;
+ return raw_dest;
+}
+
+godot_bool GDAPI godot_vector2i_operator_equal(const godot_vector2i *p_self, const godot_vector2i *p_b) {
+ const Vector2i *self = (const Vector2i *)p_self;
+ const Vector2i *b = (const Vector2i *)p_b;
+ return *self == *b;
+}
+
+godot_bool GDAPI godot_vector2i_operator_less(const godot_vector2i *p_self, const godot_vector2i *p_b) {
+ const Vector2i *self = (const Vector2i *)p_self;
+ const Vector2i *b = (const Vector2i *)p_b;
+ return *self < *b;
+}
+
+godot_vector2i GDAPI godot_vector2i_operator_neg(const godot_vector2i *p_self) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ const Vector2i *self = (const Vector2i *)p_self;
+ *dest = -(*self);
+ return raw_dest;
+}
+
+void GDAPI godot_vector2i_set_x(godot_vector2i *p_self, const godot_int p_x) {
+ Vector2i *self = (Vector2i *)p_self;
+ self->x = p_x;
+}
+
+void GDAPI godot_vector2i_set_y(godot_vector2i *p_self, const godot_int p_y) {
+ Vector2i *self = (Vector2i *)p_self;
+ self->y = p_y;
+}
+
+godot_int GDAPI godot_vector2i_get_x(const godot_vector2i *p_self) {
+ const Vector2i *self = (const Vector2i *)p_self;
+ return self->x;
+}
+
+godot_int GDAPI godot_vector2i_get_y(const godot_vector2i *p_self) {
+ const Vector2i *self = (const Vector2i *)p_self;
+ return self->y;
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp
index 3e272ae9df..32cad30c17 100644
--- a/modules/gdnative/gdnative/vector3.cpp
+++ b/modules/gdnative/gdnative/vector3.cpp
@@ -37,6 +37,11 @@
extern "C" {
#endif
+static_assert(sizeof(godot_vector3) == sizeof(Vector3), "Vector3 size mismatch");
+static_assert(sizeof(godot_vector3i) == sizeof(Vector3i), "Vector3i size mismatch");
+
+// Vector3
+
void GDAPI godot_vector3_new(godot_vector3 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z) {
Vector3 *dest = (Vector3 *)r_dest;
*dest = Vector3(p_x, p_y, p_z);
@@ -49,6 +54,13 @@ godot_string GDAPI godot_vector3_as_string(const godot_vector3 *p_self) {
return ret;
}
+godot_vector3i GDAPI godot_vector3_as_vector3i(const godot_vector3 *p_self) {
+ godot_vector3i dest;
+ const Vector3 *self = (const Vector3 *)p_self;
+ *((Vector3i *)&dest) = Vector3i(*self);
+ return dest;
+}
+
godot_int GDAPI godot_vector3_min_axis(const godot_vector3 *p_self) {
const Vector3 *self = (const Vector3 *)p_self;
return self->min_axis();
@@ -167,6 +179,13 @@ godot_vector3 GDAPI godot_vector3_abs(const godot_vector3 *p_self) {
return dest;
}
+godot_vector3 GDAPI godot_vector3_sign(const godot_vector3 *p_self) {
+ godot_vector3 dest;
+ const Vector3 *self = (const Vector3 *)p_self;
+ *((Vector3 *)&dest) = self->sign();
+ return dest;
+}
+
godot_vector3 GDAPI godot_vector3_floor(const godot_vector3 *p_self) {
godot_vector3 dest;
const Vector3 *self = (const Vector3 *)p_self;
@@ -313,6 +332,133 @@ godot_real GDAPI godot_vector3_get_axis(const godot_vector3 *p_self, const godot
return self->get_axis(p_axis);
}
+// Vector3i
+
+void GDAPI godot_vector3i_new(godot_vector3i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_z) {
+ Vector3i *dest = (Vector3i *)r_dest;
+ *dest = Vector3i(p_x, p_y, p_z);
+}
+
+godot_string GDAPI godot_vector3i_as_string(const godot_vector3i *p_self) {
+ godot_string ret;
+ const Vector3i *self = (const Vector3i *)p_self;
+ memnew_placement(&ret, String(*self));
+ return ret;
+}
+
+godot_vector3 GDAPI godot_vector3i_as_vector3(const godot_vector3i *p_self) {
+ godot_vector3 dest;
+ const Vector3i *self = (const Vector3i *)p_self;
+ *((Vector3 *)&dest) = Vector3(*self);
+ return dest;
+}
+
+godot_int GDAPI godot_vector3i_min_axis(const godot_vector3i *p_self) {
+ const Vector3i *self = (const Vector3i *)p_self;
+ return self->min_axis();
+}
+
+godot_int GDAPI godot_vector3i_max_axis(const godot_vector3i *p_self) {
+ const Vector3i *self = (const Vector3i *)p_self;
+ return self->max_axis();
+}
+
+godot_vector3i GDAPI godot_vector3i_abs(const godot_vector3i *p_self) {
+ godot_vector3i dest;
+ const Vector3i *self = (const Vector3i *)p_self;
+ *((Vector3i *)&dest) = self->abs();
+ return dest;
+}
+
+godot_vector3i GDAPI godot_vector3i_sign(const godot_vector3i *p_self) {
+ godot_vector3i dest;
+ const Vector3i *self = (const Vector3i *)p_self;
+ *((Vector3i *)&dest) = self->sign();
+ return dest;
+}
+
+godot_vector3i GDAPI godot_vector3i_operator_add(const godot_vector3i *p_self, const godot_vector3i *p_b) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ Vector3i *self = (Vector3i *)p_self;
+ const Vector3i *b = (const Vector3i *)p_b;
+ *dest = *self + *b;
+ return raw_dest;
+}
+
+godot_vector3i GDAPI godot_vector3i_operator_subtract(const godot_vector3i *p_self, const godot_vector3i *p_b) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ Vector3i *self = (Vector3i *)p_self;
+ const Vector3i *b = (const Vector3i *)p_b;
+ *dest = *self - *b;
+ return raw_dest;
+}
+
+godot_vector3i GDAPI godot_vector3i_operator_multiply_vector(const godot_vector3i *p_self, const godot_vector3i *p_b) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ Vector3i *self = (Vector3i *)p_self;
+ const Vector3i *b = (const Vector3i *)p_b;
+ *dest = *self * *b;
+ return raw_dest;
+}
+
+godot_vector3i GDAPI godot_vector3i_operator_multiply_scalar(const godot_vector3i *p_self, const godot_int p_b) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ Vector3i *self = (Vector3i *)p_self;
+ *dest = *self * p_b;
+ return raw_dest;
+}
+
+godot_vector3i GDAPI godot_vector3i_operator_divide_vector(const godot_vector3i *p_self, const godot_vector3i *p_b) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ Vector3i *self = (Vector3i *)p_self;
+ const Vector3i *b = (const Vector3i *)p_b;
+ *dest = *self / *b;
+ return raw_dest;
+}
+
+godot_vector3i GDAPI godot_vector3i_operator_divide_scalar(const godot_vector3i *p_self, const godot_int p_b) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ Vector3i *self = (Vector3i *)p_self;
+ *dest = *self / p_b;
+ return raw_dest;
+}
+
+godot_bool GDAPI godot_vector3i_operator_equal(const godot_vector3i *p_self, const godot_vector3i *p_b) {
+ Vector3i *self = (Vector3i *)p_self;
+ const Vector3i *b = (const Vector3i *)p_b;
+ return *self == *b;
+}
+
+godot_bool GDAPI godot_vector3i_operator_less(const godot_vector3i *p_self, const godot_vector3i *p_b) {
+ Vector3i *self = (Vector3i *)p_self;
+ const Vector3i *b = (const Vector3i *)p_b;
+ return *self < *b;
+}
+
+godot_vector3i GDAPI godot_vector3i_operator_neg(const godot_vector3i *p_self) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ const Vector3i *self = (const Vector3i *)p_self;
+ *dest = -(*self);
+ return raw_dest;
+}
+
+void GDAPI godot_vector3i_set_axis(godot_vector3i *p_self, const godot_vector3_axis p_axis, const godot_int p_val) {
+ Vector3i *self = (Vector3i *)p_self;
+ self->set_axis(p_axis, p_val);
+}
+
+godot_int GDAPI godot_vector3i_get_axis(const godot_vector3i *p_self, const godot_vector3_axis p_axis) {
+ const Vector3i *self = (const Vector3i *)p_self;
+ return self->get_axis(p_axis);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index d5ab62dc61..82bfbd23de 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -2,1130 +2,587 @@
"core": {
"type": "CORE",
"version": {
- "major": 1,
+ "major": 4,
"minor": 0
},
- "next": {
- "type": "CORE",
- "version": {
- "major": 1,
- "minor": 1
- },
- "next": {
- "type": "CORE",
- "version": {
- "major": 1,
- "minor": 2
- },
- "next": null,
- "api": [
- {
- "name": "godot_dictionary_duplicate",
- "return_type": "godot_dictionary",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_bool", "p_deep"]
- ]
- },
- {
- "name": "godot_vector3_move_toward",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_to"],
- ["const godot_real", "p_delta"]
- ]
- },
- {
- "name": "godot_vector2_move_toward",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"],
- ["const godot_real", "p_delta"]
- ]
- },
- {
- "name": "godot_string_count",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
- ["godot_int", "p_from"],
- ["godot_int", "p_to"]
- ]
- },
- {
- "name": "godot_string_countn",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
- ["godot_int", "p_from"],
- ["godot_int", "p_to"]
- ]
- },
- {
- "name": "godot_vector3_direction_to",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector2_direction_to",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
- ]
- },
- {
- "name": "godot_array_slice",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_int", "p_begin"],
- ["const godot_int", "p_end"],
- ["const godot_int", "p_step"],
- ["const godot_bool", "p_deep"]
- ]
- },
- {
- "name": "godot_packed_byte_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_int_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_real_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_real_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_get_class_tag",
- "return_type": "void *",
- "arguments": [
- ["const godot_string_name *", "p_class"]
- ]
- },
- {
- "name": "godot_object_cast_to",
- "return_type": "godot_object *",
- "arguments": [
- ["const godot_object *", "p_object"],
- ["void *", "p_class_tag"]
- ]
- },
- {
- "name": "godot_instance_from_id",
- "return_type": "godot_object *",
- "arguments": [
- ["godot_int", "p_instance_id"]
- ]
- }
- ]
- },
- "api": [
- {
- "name": "godot_color_to_abgr32",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_abgr64",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_argb64",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_rgba64",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_darkened",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_real", "p_amount"]
- ]
- },
- {
- "name": "godot_color_from_hsv",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_real", "p_h"],
- ["const godot_real", "p_s"],
- ["const godot_real", "p_v"],
- ["const godot_real", "p_a"]
- ]
- },
- {
- "name": "godot_color_lightened",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_real", "p_amount"]
- ]
- },
- {
- "name": "godot_array_duplicate",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_bool", "p_deep"]
- ]
- },
- {
- "name": "godot_array_max",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_min",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_shuffle",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_slerp",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_basis *", "p_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_dictionary_get_with_default",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"],
- ["const godot_variant *", "p_default"]
- ]
- },
- {
- "name": "godot_dictionary_erase_with_return",
- "return_type": "bool",
- "arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_node_path_get_as_property_path",
- "return_type": "godot_node_path",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_set_axis_angle",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_angle"]
- ]
- },
- {
- "name": "godot_rect2_grow_individual",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_real", "p_left"],
- ["const godot_real", "p_top"],
- ["const godot_real", "p_right"],
- ["const godot_real", "p_bottom"]
- ]
- },
- {
- "name": "godot_rect2_grow_margin",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_int", "p_margin"],
- ["const godot_real", "p_by"]
- ]
- },
- {
- "name": "godot_rect2_abs",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_string_dedent",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_trim_prefix",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_prefix"]
- ]
- },
- {
- "name": "godot_string_trim_suffix",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_suffix"]
- ]
- },
- {
- "name": "godot_string_rstrip",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_chars"]
- ]
- },
- {
- "name": "godot_string_rsplit",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_divisor"],
- ["const godot_bool", "p_allow_empty"],
- ["const godot_int", "p_maxsplit"]
- ]
- },
- {
- "name": "godot_basis_get_quat",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_set_quat",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_quat *", "p_quat"]
- ]
- },
- {
- "name": "godot_basis_set_axis_angle_scale",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["godot_real", "p_phi"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_basis_set_euler_scale",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_euler"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_basis_set_quat_scale",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_quat *", "p_quat"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_quat_new_with_basis",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_basis *", "p_basis"]
- ]
- },
- {
- "name": "godot_quat_new_with_euler",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_vector3 *", "p_euler"]
- ]
- },
- {
- "name": "godot_transform_new_with_quat",
- "return_type": "void",
- "arguments": [
- ["godot_transform *", "r_dest"],
- ["const godot_quat *", "p_quat"]
- ]
- },
- {
- "name": "godot_variant_get_operator_name",
- "return_type": "godot_string",
- "arguments": [
- ["godot_variant_operator", "p_op"]
- ]
- },
- {
- "name": "godot_variant_evaluate",
- "return_type": "void",
- "arguments": [
- ["godot_variant_operator", "p_op"],
- ["const godot_variant *", "p_a"],
- ["const godot_variant *", "p_b"],
- ["godot_variant *", "r_ret"],
- ["godot_bool *", "r_valid"]
- ]
- }
- ]
- },
+ "next": null,
"api": [
{
- "name": "godot_color_new_rgba",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "r_dest"],
- ["const godot_real", "p_r"],
- ["const godot_real", "p_g"],
- ["const godot_real", "p_b"],
- ["const godot_real", "p_a"]
- ]
- },
- {
- "name": "godot_color_new_rgb",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "r_dest"],
- ["const godot_real", "p_r"],
- ["const godot_real", "p_g"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_color_get_r",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_set_r",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "r"]
- ]
- },
- {
- "name": "godot_color_get_g",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_set_g",
+ "name": "godot_aabb_new",
"return_type": "void",
"arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "g"]
+ ["godot_aabb *", "r_dest"],
+ ["const godot_vector3 *", "p_pos"],
+ ["const godot_vector3 *", "p_size"]
]
},
{
- "name": "godot_color_get_b",
- "return_type": "godot_real",
+ "name": "godot_aabb_get_position",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_color *", "p_self"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_color_set_b",
+ "name": "godot_aabb_set_position",
"return_type": "void",
"arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "b"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_vector3 *", "p_v"]
]
},
{
- "name": "godot_color_get_a",
- "return_type": "godot_real",
+ "name": "godot_aabb_get_size",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_color *", "p_self"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_color_set_a",
+ "name": "godot_aabb_set_size",
"return_type": "void",
"arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "a"]
- ]
- },
- {
- "name": "godot_color_get_h",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_get_s",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_get_v",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_vector3 *", "p_v"]
]
},
{
- "name": "godot_color_as_string",
+ "name": "godot_aabb_as_string",
"return_type": "godot_string",
"arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_rgba32",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_argb32",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_inverted",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_contrasted",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_lerp",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_b"],
- ["const godot_real", "p_t"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_color_blend",
- "return_type": "godot_color",
+ "name": "godot_aabb_abs",
+ "return_type": "godot_aabb",
"arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_over"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_color_to_html",
- "return_type": "godot_string",
+ "name": "godot_aabb_get_area",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_bool", "p_with_alpha"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_color_operator_equal",
+ "name": "godot_aabb_has_no_area",
"return_type": "godot_bool",
"arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_b"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_color_operator_less",
+ "name": "godot_aabb_has_no_surface",
"return_type": "godot_bool",
"arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_b"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_vector2_new",
- "return_type": "void",
+ "name": "godot_aabb_intersects",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_vector2 *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_aabb *", "p_with"]
]
},
{
- "name": "godot_vector2_as_string",
- "return_type": "godot_string",
+ "name": "godot_aabb_encloses",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_aabb *", "p_with"]
]
},
{
- "name": "godot_vector2_normalized",
- "return_type": "godot_vector2",
+ "name": "godot_aabb_merge",
+ "return_type": "godot_aabb",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_aabb *", "p_with"]
]
},
{
- "name": "godot_vector2_length",
- "return_type": "godot_real",
+ "name": "godot_aabb_intersection",
+ "return_type": "godot_aabb",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_aabb *", "p_with"]
]
},
{
- "name": "godot_vector2_angle",
- "return_type": "godot_real",
+ "name": "godot_aabb_intersects_plane",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_plane *", "p_plane"]
]
},
{
- "name": "godot_vector2_length_squared",
- "return_type": "godot_real",
+ "name": "godot_aabb_intersects_segment",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_vector3 *", "p_from"],
+ ["const godot_vector3 *", "p_to"]
]
},
{
- "name": "godot_vector2_is_normalized",
+ "name": "godot_aabb_has_point",
"return_type": "godot_bool",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_vector3 *", "p_point"]
]
},
{
- "name": "godot_vector2_distance_to",
- "return_type": "godot_real",
+ "name": "godot_aabb_get_support",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_vector3 *", "p_dir"]
]
},
{
- "name": "godot_vector2_distance_squared_to",
- "return_type": "godot_real",
+ "name": "godot_aabb_get_longest_axis",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_vector2_angle_to",
- "return_type": "godot_real",
+ "name": "godot_aabb_get_longest_axis_index",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_vector2_angle_to_point",
+ "name": "godot_aabb_get_longest_axis_size",
"return_type": "godot_real",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_vector2_lerp",
- "return_type": "godot_vector2",
+ "name": "godot_aabb_get_shortest_axis",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"],
- ["const godot_real", "p_t"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_vector2_cubic_interpolate",
- "return_type": "godot_vector2",
+ "name": "godot_aabb_get_shortest_axis_index",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"],
- ["const godot_vector2 *", "p_pre_a"],
- ["const godot_vector2 *", "p_post_b"],
- ["const godot_real", "p_t"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_vector2_rotated",
- "return_type": "godot_vector2",
+ "name": "godot_aabb_get_shortest_axis_size",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_phi"]
+ ["const godot_aabb *", "p_self"]
]
},
{
- "name": "godot_vector2_tangent",
- "return_type": "godot_vector2",
+ "name": "godot_aabb_expand",
+ "return_type": "godot_aabb",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_vector3 *", "p_to_point"]
]
},
{
- "name": "godot_vector2_floor",
- "return_type": "godot_vector2",
+ "name": "godot_aabb_grow",
+ "return_type": "godot_aabb",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_real", "p_by"]
]
},
{
- "name": "godot_vector2_snapped",
- "return_type": "godot_vector2",
+ "name": "godot_aabb_get_endpoint",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_by"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_int", "p_idx"]
]
},
{
- "name": "godot_vector2_aspect",
- "return_type": "godot_real",
+ "name": "godot_aabb_operator_equal",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_aabb *", "p_self"],
+ ["const godot_aabb *", "p_b"]
]
},
{
- "name": "godot_vector2_dot",
- "return_type": "godot_real",
+ "name": "godot_array_new",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_with"]
+ ["godot_array *", "r_dest"]
]
},
{
- "name": "godot_vector2_slide",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_copy",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_n"]
+ ["godot_array *", "r_dest"],
+ ["const godot_array *", "p_src"]
]
},
{
- "name": "godot_vector2_bounce",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_color_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_n"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_color_array *", "p_pca"]
]
},
{
- "name": "godot_vector2_reflect",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_vector3_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_n"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_vector3_array *", "p_pv3a"]
]
},
{
- "name": "godot_vector2_abs",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_vector2_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_vector2_array *", "p_pv2a"]
]
},
{
- "name": "godot_vector2_clamped",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_string_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_length"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_string_array *", "p_psa"]
]
},
{
- "name": "godot_vector2_operator_add",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_float32_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_float32_array *", "p_pra"]
]
},
{
- "name": "godot_vector2_operator_subtract",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_float64_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_float64_array *", "p_pra"]
]
},
{
- "name": "godot_vector2_operator_multiply_vector",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_int32_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_int32_array *", "p_pia"]
]
},
{
- "name": "godot_vector2_operator_multiply_scalar",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_int64_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_b"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_int64_array *", "p_pia"]
]
},
{
- "name": "godot_vector2_operator_divide_vector",
- "return_type": "godot_vector2",
+ "name": "godot_array_new_packed_byte_array",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
+ ["godot_array *", "r_dest"],
+ ["const godot_packed_byte_array *", "p_pba"]
]
},
{
- "name": "godot_vector2_operator_divide_scalar",
- "return_type": "godot_vector2",
+ "name": "godot_array_set",
+ "return_type": "void",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_b"]
+ ["godot_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_vector2_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_array_get",
+ "return_type": "godot_variant",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
+ ["const godot_array *", "p_self"],
+ ["const godot_int", "p_idx"]
]
},
{
- "name": "godot_vector2_operator_less",
- "return_type": "godot_bool",
+ "name": "godot_array_operator_index",
+ "return_type": "godot_variant *",
"arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
+ ["godot_array *", "p_self"],
+ ["const godot_int", "p_idx"]
]
},
{
- "name": "godot_vector2_operator_neg",
- "return_type": "godot_vector2",
+ "name": "godot_array_operator_index_const",
+ "return_type": "const godot_variant *",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_array *", "p_self"],
+ ["const godot_int", "p_idx"]
]
},
{
- "name": "godot_vector2_set_x",
+ "name": "godot_array_append",
"return_type": "void",
"arguments": [
- ["godot_vector2 *", "p_self"],
- ["const godot_real", "p_x"]
+ ["godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_vector2_set_y",
+ "name": "godot_array_clear",
"return_type": "void",
"arguments": [
- ["godot_vector2 *", "p_self"],
- ["const godot_real", "p_y"]
+ ["godot_array *", "p_self"]
]
},
{
- "name": "godot_vector2_get_x",
- "return_type": "godot_real",
+ "name": "godot_array_count",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_vector2_get_y",
- "return_type": "godot_real",
+ "name": "godot_array_duplicate",
+ "return_type": "godot_array",
"arguments": [
- ["const godot_vector2 *", "p_self"]
+ ["const godot_array *", "p_self"],
+ ["const godot_bool", "p_deep"]
]
},
{
- "name": "godot_quat_new",
- "return_type": "void",
+ "name": "godot_array_empty",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"],
- ["const godot_real", "p_z"],
- ["const godot_real", "p_w"]
+ ["const godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_new_with_axis_angle",
+ "name": "godot_array_erase",
"return_type": "void",
"arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_angle"]
+ ["godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_quat_get_x",
- "return_type": "godot_real",
+ "name": "godot_array_front",
+ "return_type": "godot_variant",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["const godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_set_x",
- "return_type": "void",
+ "name": "godot_array_back",
+ "return_type": "godot_variant",
"arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
+ ["const godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_get_y",
- "return_type": "godot_real",
+ "name": "godot_array_find",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["const godot_array *", "p_self"],
+ ["const godot_variant *", "p_what"],
+ ["const godot_int", "p_from"]
]
},
{
- "name": "godot_quat_set_y",
- "return_type": "void",
+ "name": "godot_array_find_last",
+ "return_type": "godot_int",
"arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
+ ["const godot_array *", "p_self"],
+ ["const godot_variant *", "p_what"]
]
},
{
- "name": "godot_quat_get_z",
- "return_type": "godot_real",
+ "name": "godot_array_has",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["const godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_quat_set_z",
- "return_type": "void",
+ "name": "godot_array_hash",
+ "return_type": "godot_int",
"arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
+ ["const godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_get_w",
- "return_type": "godot_real",
+ "name": "godot_array_insert",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["godot_array *", "p_self"],
+ ["const godot_int", "p_pos"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_quat_set_w",
+ "name": "godot_array_invert",
"return_type": "void",
"arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
+ ["godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_as_string",
- "return_type": "godot_string",
+ "name": "godot_array_max",
+ "return_type": "godot_variant",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["const godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_length",
- "return_type": "godot_real",
+ "name": "godot_array_min",
+ "return_type": "godot_variant",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["const godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_length_squared",
- "return_type": "godot_real",
+ "name": "godot_array_pop_back",
+ "return_type": "godot_variant",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_normalized",
- "return_type": "godot_quat",
+ "name": "godot_array_pop_front",
+ "return_type": "godot_variant",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_is_normalized",
- "return_type": "godot_bool",
+ "name": "godot_array_push_back",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_quat_inverse",
- "return_type": "godot_quat",
+ "name": "godot_array_push_front",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"]
]
},
{
- "name": "godot_quat_dot",
- "return_type": "godot_real",
+ "name": "godot_array_remove",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
+ ["godot_array *", "p_self"],
+ ["const godot_int", "p_idx"]
]
},
{
- "name": "godot_quat_xform",
- "return_type": "godot_vector3",
+ "name": "godot_array_resize",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_vector3 *", "p_v"]
+ ["godot_array *", "p_self"],
+ ["const godot_int", "p_size"]
]
},
{
- "name": "godot_quat_slerp",
- "return_type": "godot_quat",
+ "name": "godot_array_rfind",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"],
- ["const godot_real", "p_t"]
+ ["const godot_array *", "p_self"],
+ ["const godot_variant *", "p_what"],
+ ["const godot_int", "p_from"]
]
},
{
- "name": "godot_quat_slerpni",
- "return_type": "godot_quat",
+ "name": "godot_array_size",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"],
- ["const godot_real", "p_t"]
+ ["const godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_cubic_slerp",
- "return_type": "godot_quat",
+ "name": "godot_array_shuffle",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"],
- ["const godot_quat *", "p_pre_a"],
- ["const godot_quat *", "p_post_b"],
- ["const godot_real", "p_t"]
+ ["godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_operator_multiply",
- "return_type": "godot_quat",
+ "name": "godot_array_slice",
+ "return_type": "godot_array",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_real", "p_b"]
+ ["const godot_array *", "p_self"],
+ ["const godot_int", "p_begin"],
+ ["const godot_int", "p_end"],
+ ["const godot_int", "p_step"],
+ ["const godot_bool", "p_deep"]
]
},
{
- "name": "godot_quat_operator_add",
- "return_type": "godot_quat",
+ "name": "godot_array_sort",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
+ ["godot_array *", "p_self"]
]
},
{
- "name": "godot_quat_operator_subtract",
- "return_type": "godot_quat",
+ "name": "godot_array_sort_custom",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
+ ["godot_array *", "p_self"],
+ ["godot_object *", "p_obj"],
+ ["const godot_string *", "p_func"]
]
},
{
- "name": "godot_quat_operator_divide",
- "return_type": "godot_quat",
+ "name": "godot_array_bsearch",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_real", "p_b"]
+ ["godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"],
+ ["const godot_bool", "p_before"]
]
},
{
- "name": "godot_quat_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_array_bsearch_custom",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
+ ["godot_array *", "p_self"],
+ ["const godot_variant *", "p_value"],
+ ["godot_object *", "p_obj"],
+ ["const godot_string *", "p_func"],
+ ["const godot_bool", "p_before"]
]
},
{
- "name": "godot_quat_operator_neg",
- "return_type": "godot_quat",
+ "name": "godot_array_destroy",
+ "return_type": "void",
"arguments": [
- ["const godot_quat *", "p_self"]
+ ["godot_array *", "p_self"]
]
},
{
@@ -1366,294 +823,803 @@
]
},
{
- "name": "godot_vector3_new",
+ "name": "godot_basis_slerp",
+ "return_type": "godot_basis",
+ "arguments": [
+ ["const godot_basis *", "p_self"],
+ ["const godot_basis *", "p_b"],
+ ["const godot_real", "p_t"]
+ ]
+ },
+ {
+ "name": "godot_basis_get_quat",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_basis *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_basis_set_quat",
"return_type": "void",
"arguments": [
- ["godot_vector3 *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"],
- ["const godot_real", "p_z"]
+ ["godot_basis *", "p_self"],
+ ["const godot_quat *", "p_quat"]
]
},
{
- "name": "godot_vector3_as_string",
+ "name": "godot_basis_set_axis_angle_scale",
+ "return_type": "void",
+ "arguments": [
+ ["godot_basis *", "p_self"],
+ ["const godot_vector3 *", "p_axis"],
+ ["godot_real", "p_phi"],
+ ["const godot_vector3 *", "p_scale"]
+ ]
+ },
+ {
+ "name": "godot_basis_set_euler_scale",
+ "return_type": "void",
+ "arguments": [
+ ["godot_basis *", "p_self"],
+ ["const godot_vector3 *", "p_euler"],
+ ["const godot_vector3 *", "p_scale"]
+ ]
+ },
+ {
+ "name": "godot_basis_set_quat_scale",
+ "return_type": "void",
+ "arguments": [
+ ["godot_basis *", "p_self"],
+ ["const godot_quat *", "p_quat"],
+ ["const godot_vector3 *", "p_scale"]
+ ]
+ },
+ {
+ "name": "godot_callable_new_with_object",
+ "return_type": "void",
+ "arguments": [
+ ["godot_callable *", "r_dest"],
+ ["const godot_object *", "p_object"],
+ ["const godot_string_name *", "p_method"]
+ ]
+ },
+ {
+ "name": "godot_callable_new_with_object_id",
+ "return_type": "void",
+ "arguments": [
+ ["godot_callable *", "r_dest"],
+ ["uint64_t", "p_objectid"],
+ ["const godot_string_name *", "p_method"]
+ ]
+ },
+ {
+ "name": "godot_callable_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_callable *", "r_dest"],
+ ["const godot_callable *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_callable_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_call",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_callable *", "p_self"],
+ ["const godot_variant **", "p_arguments"],
+ ["godot_int", "p_argcount"],
+ ["godot_variant *", "r_return_value"]
+ ]
+ },
+ {
+ "name": "godot_callable_call_deferred",
+ "return_type": "void",
+ "arguments": [
+ ["const godot_callable *", "p_self"],
+ ["const godot_variant **", "p_arguments"],
+ ["godot_int", "p_argcount"]
+ ]
+ },
+ {
+ "name": "godot_callable_is_null",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_is_custom",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_is_standard",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_get_object",
+ "return_type": "godot_object *",
+ "arguments": [
+ ["const godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_get_object_id",
+ "return_type": "uint64_t",
+ "arguments": [
+ ["const godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_get_method",
+ "return_type": "godot_string_name",
+ "arguments": [
+ ["const godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_hash",
+ "return_type": "uint32_t",
+ "arguments": [
+ ["const godot_callable *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_callable_as_string",
"return_type": "godot_string",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_callable *", "p_self"]
]
},
{
- "name": "godot_vector3_min_axis",
+ "name": "godot_callable_operator_equal",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_callable *", "p_self"],
+ ["const godot_callable *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_callable_operator_less",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_callable *", "p_self"],
+ ["const godot_callable *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_signal_new_with_object",
+ "return_type": "void",
+ "arguments": [
+ ["godot_signal *", "r_dest"],
+ ["const godot_object *", "p_object"],
+ ["const godot_string_name *", "p_method"]
+ ]
+ },
+ {
+ "name": "godot_signal_new_with_object_id",
+ "return_type": "void",
+ "arguments": [
+ ["godot_signal *", "r_dest"],
+ ["uint64_t", "p_objectid"],
+ ["const godot_string_name *", "p_method"]
+ ]
+ },
+ {
+ "name": "godot_signal_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_signal *", "r_dest"],
+ ["const godot_signal *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_signal_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_signal *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_signal_emit",
"return_type": "godot_int",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_signal *", "p_self"],
+ ["const godot_variant **", "p_arguments"],
+ ["godot_int", "p_argcount"]
]
},
{
- "name": "godot_vector3_max_axis",
+ "name": "godot_signal_connect",
"return_type": "godot_int",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["godot_signal *", "p_self"],
+ ["const godot_callable *", "p_callable"],
+ ["const godot_array *", "p_binds"],
+ ["uint32_t", "p_flags"]
]
},
{
- "name": "godot_vector3_length",
- "return_type": "godot_real",
+ "name": "godot_signal_disconnect",
+ "return_type": "void",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["godot_signal *", "p_self"],
+ ["const godot_callable *", "p_callable"]
]
},
{
- "name": "godot_vector3_length_squared",
- "return_type": "godot_real",
+ "name": "godot_signal_is_null",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_signal *", "p_self"]
]
},
{
- "name": "godot_vector3_is_normalized",
+ "name": "godot_signal_is_connected",
"return_type": "godot_bool",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_signal *", "p_self"],
+ ["const godot_callable *", "p_callable"]
]
},
{
- "name": "godot_vector3_normalized",
- "return_type": "godot_vector3",
+ "name": "godot_signal_get_connections",
+ "return_type": "godot_array",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_signal *", "p_self"]
]
},
{
- "name": "godot_vector3_inverse",
- "return_type": "godot_vector3",
+ "name": "godot_signal_get_object",
+ "return_type": "godot_object *",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_signal *", "p_self"]
]
},
{
- "name": "godot_vector3_snapped",
- "return_type": "godot_vector3",
+ "name": "godot_signal_get_object_id",
+ "return_type": "uint64_t",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_by"]
+ ["const godot_signal *", "p_self"]
]
},
{
- "name": "godot_vector3_rotated",
- "return_type": "godot_vector3",
+ "name": "godot_signal_get_name",
+ "return_type": "godot_string_name",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_phi"]
+ ["const godot_signal *", "p_self"]
]
},
{
- "name": "godot_vector3_lerp",
- "return_type": "godot_vector3",
+ "name": "godot_signal_as_string",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"],
- ["const godot_real", "p_t"]
+ ["const godot_signal *", "p_self"]
]
},
{
- "name": "godot_vector3_cubic_interpolate",
- "return_type": "godot_vector3",
+ "name": "godot_signal_operator_equal",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"],
- ["const godot_vector3 *", "p_pre_a"],
- ["const godot_vector3 *", "p_post_b"],
- ["const godot_real", "p_t"]
+ ["const godot_signal *", "p_self"],
+ ["const godot_signal *", "p_other"]
]
},
{
- "name": "godot_vector3_dot",
+ "name": "godot_signal_operator_less",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_signal *", "p_self"],
+ ["const godot_signal *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_color_new_rgba",
+ "return_type": "void",
+ "arguments": [
+ ["godot_color *", "r_dest"],
+ ["const godot_real", "p_r"],
+ ["const godot_real", "p_g"],
+ ["const godot_real", "p_b"],
+ ["const godot_real", "p_a"]
+ ]
+ },
+ {
+ "name": "godot_color_new_rgb",
+ "return_type": "void",
+ "arguments": [
+ ["godot_color *", "r_dest"],
+ ["const godot_real", "p_r"],
+ ["const godot_real", "p_g"],
+ ["const godot_real", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_color_get_r",
"return_type": "godot_real",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_cross",
- "return_type": "godot_vector3",
+ "name": "godot_color_set_r",
+ "return_type": "void",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["godot_color *", "p_self"],
+ ["const godot_real", "r"]
]
},
{
- "name": "godot_vector3_outer",
- "return_type": "godot_basis",
+ "name": "godot_color_get_g",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_to_diagonal_matrix",
- "return_type": "godot_basis",
+ "name": "godot_color_set_g",
+ "return_type": "void",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["godot_color *", "p_self"],
+ ["const godot_real", "g"]
]
},
{
- "name": "godot_vector3_abs",
- "return_type": "godot_vector3",
+ "name": "godot_color_get_b",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_floor",
- "return_type": "godot_vector3",
+ "name": "godot_color_set_b",
+ "return_type": "void",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["godot_color *", "p_self"],
+ ["const godot_real", "b"]
]
},
{
- "name": "godot_vector3_ceil",
- "return_type": "godot_vector3",
+ "name": "godot_color_get_a",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_distance_to",
+ "name": "godot_color_set_a",
+ "return_type": "void",
+ "arguments": [
+ ["godot_color *", "p_self"],
+ ["const godot_real", "a"]
+ ]
+ },
+ {
+ "name": "godot_color_get_h",
"return_type": "godot_real",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_distance_squared_to",
+ "name": "godot_color_get_s",
"return_type": "godot_real",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_angle_to",
+ "name": "godot_color_get_v",
"return_type": "godot_real",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_to"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_slide",
- "return_type": "godot_vector3",
+ "name": "godot_color_as_string",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_n"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_bounce",
- "return_type": "godot_vector3",
+ "name": "godot_color_to_rgba32",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_n"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_reflect",
- "return_type": "godot_vector3",
+ "name": "godot_color_to_argb32",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_n"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_operator_add",
- "return_type": "godot_vector3",
+ "name": "godot_color_inverted",
+ "return_type": "godot_color",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_operator_subtract",
- "return_type": "godot_vector3",
+ "name": "godot_color_contrasted",
+ "return_type": "godot_color",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"]
]
},
{
- "name": "godot_vector3_operator_multiply_vector",
- "return_type": "godot_vector3",
+ "name": "godot_color_lerp",
+ "return_type": "godot_color",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"],
+ ["const godot_color *", "p_b"],
+ ["const godot_real", "p_t"]
]
},
{
- "name": "godot_vector3_operator_multiply_scalar",
- "return_type": "godot_vector3",
+ "name": "godot_color_blend",
+ "return_type": "godot_color",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_real", "p_b"]
+ ["const godot_color *", "p_self"],
+ ["const godot_color *", "p_over"]
]
},
{
- "name": "godot_vector3_operator_divide_vector",
- "return_type": "godot_vector3",
+ "name": "godot_color_to_html",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"],
+ ["const godot_bool", "p_with_alpha"]
]
},
{
- "name": "godot_vector3_operator_divide_scalar",
- "return_type": "godot_vector3",
+ "name": "godot_color_operator_equal",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_real", "p_b"]
+ ["const godot_color *", "p_self"],
+ ["const godot_color *", "p_b"]
]
},
{
- "name": "godot_vector3_operator_equal",
+ "name": "godot_color_operator_less",
"return_type": "godot_bool",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_color *", "p_self"],
+ ["const godot_color *", "p_b"]
]
},
{
- "name": "godot_vector3_operator_less",
+ "name": "godot_color_to_abgr32",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_abgr64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_argb64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_to_rgba64",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_color *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_color_darkened",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_amount"]
+ ]
+ },
+ {
+ "name": "godot_color_from_hsv",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_h"],
+ ["const godot_real", "p_s"],
+ ["const godot_real", "p_v"],
+ ["const godot_real", "p_a"]
+ ]
+ },
+ {
+ "name": "godot_color_lightened",
+ "return_type": "godot_color",
+ "arguments": [
+ ["const godot_color *", "p_self"],
+ ["const godot_real", "p_amount"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_new",
+ "return_type": "void",
+ "arguments": [
+ ["godot_dictionary *", "r_dest"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_dictionary *", "r_dest"],
+ ["const godot_dictionary *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_dictionary *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_size",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_empty",
"return_type": "godot_bool",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
+ ["const godot_dictionary *", "p_self"]
]
},
{
- "name": "godot_vector3_operator_neg",
- "return_type": "godot_vector3",
+ "name": "godot_dictionary_clear",
+ "return_type": "void",
"arguments": [
- ["const godot_vector3 *", "p_self"]
+ ["godot_dictionary *", "p_self"]
]
},
{
- "name": "godot_vector3_set_axis",
+ "name": "godot_dictionary_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_has_all",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_array *", "p_keys"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_erase",
"return_type": "void",
"arguments": [
- ["godot_vector3 *", "p_self"],
- ["const godot_vector3_axis", "p_axis"],
- ["const godot_real", "p_val"]
+ ["godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
]
},
{
- "name": "godot_vector3_get_axis",
- "return_type": "godot_real",
+ "name": "godot_dictionary_hash",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3_axis", "p_axis"]
+ ["const godot_dictionary *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_keys",
+ "return_type": "godot_array",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_values",
+ "return_type": "godot_array",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_get",
+ "return_type": "godot_variant",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_set",
+ "return_type": "void",
+ "arguments": [
+ ["godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"],
+ ["const godot_variant *", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_operator_index",
+ "return_type": "godot_variant *",
+ "arguments": [
+ ["godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_operator_index_const",
+ "return_type": "const godot_variant *",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_next",
+ "return_type": "godot_variant *",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_operator_equal",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_dictionary *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_to_json",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_duplicate",
+ "return_type": "godot_dictionary",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_bool", "p_deep"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_get_with_default",
+ "return_type": "godot_variant",
+ "arguments": [
+ ["const godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"],
+ ["const godot_variant *", "p_default"]
+ ]
+ },
+ {
+ "name": "godot_dictionary_erase_with_return",
+ "return_type": "bool",
+ "arguments": [
+ ["godot_dictionary *", "p_self"],
+ ["const godot_variant *", "p_key"]
+ ]
+ },
+ {
+ "name": "godot_node_path_new",
+ "return_type": "void",
+ "arguments": [
+ ["godot_node_path *", "r_dest"],
+ ["const godot_string *", "p_from"]
+ ]
+ },
+ {
+ "name": "godot_node_path_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_node_path *", "r_dest"],
+ ["const godot_node_path *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_node_path_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_node_path_as_string",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_node_path_is_absolute",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_name_count",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_name",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_node_path *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_subname_count",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_subname",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_node_path *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_concatenated_subnames",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_node_path_is_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_node_path_operator_equal",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_node_path *", "p_self"],
+ ["const godot_node_path *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_node_path_get_as_property_path",
+ "return_type": "godot_node_path",
+ "arguments": [
+ ["const godot_node_path *", "p_self"]
]
},
{
@@ -1680,6 +1646,13 @@
]
},
{
+ "name": "godot_packed_byte_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_byte_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_byte_array_append",
"return_type": "void",
"arguments": [
@@ -1705,6 +1678,21 @@
]
},
{
+ "name": "godot_packed_byte_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_byte_array *", "p_self"],
+ ["const uint8_t", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_byte_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_byte_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_byte_array_invert",
"return_type": "void",
"arguments": [
@@ -1736,6 +1724,20 @@
]
},
{
+ "name": "godot_packed_byte_array_ptr",
+ "return_type": "const uint8_t *",
+ "arguments": [
+ ["const godot_packed_byte_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_byte_array_ptrw",
+ "return_type": "uint8_t *",
+ "arguments": [
+ ["godot_packed_byte_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_byte_array_set",
"return_type": "void",
"arguments": [
@@ -1767,223 +1769,587 @@
]
},
{
- "name": "godot_packed_int_array_new",
+ "name": "godot_packed_int32_array_new",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "r_dest"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "r_dest"],
+ ["const godot_packed_int32_array *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_new_with_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "r_dest"],
+ ["const godot_array *", "p_a"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_int32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_append",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const int32_t", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_append_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const godot_packed_int32_array *", "p_array"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_insert",
+ "return_type": "godot_error",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const int32_t", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const int32_t", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_invert",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_push_back",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const int32_t", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_remove",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_resize",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const godot_int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_ptr",
+ "return_type": "const int32_t *",
+ "arguments": [
+ ["const godot_packed_int32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_ptrw",
+ "return_type": "int32_t *",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_set",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const int32_t", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_get",
+ "return_type": "int32_t",
+ "arguments": [
+ ["const godot_packed_int32_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_size",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_packed_int32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_new",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "r_dest"]
+ ["godot_packed_int64_array *", "r_dest"]
]
},
{
- "name": "godot_packed_int_array_new_copy",
+ "name": "godot_packed_int64_array_new_copy",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "r_dest"],
- ["const godot_packed_int_array *", "p_src"]
+ ["godot_packed_int64_array *", "r_dest"],
+ ["const godot_packed_int64_array *", "p_src"]
]
},
{
- "name": "godot_packed_int_array_new_with_array",
+ "name": "godot_packed_int64_array_new_with_array",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "r_dest"],
+ ["godot_packed_int64_array *", "r_dest"],
["const godot_array *", "p_a"]
]
},
{
- "name": "godot_packed_int_array_append",
+ "name": "godot_packed_int64_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_int64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_append",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"],
- ["const godot_int", "p_data"]
+ ["godot_packed_int64_array *", "p_self"],
+ ["const int64_t", "p_data"]
]
},
{
- "name": "godot_packed_int_array_append_array",
+ "name": "godot_packed_int64_array_append_array",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"],
- ["const godot_packed_int_array *", "p_array"]
+ ["godot_packed_int64_array *", "p_self"],
+ ["const godot_packed_int64_array *", "p_array"]
]
},
{
- "name": "godot_packed_int_array_insert",
+ "name": "godot_packed_int64_array_insert",
"return_type": "godot_error",
"arguments": [
- ["godot_packed_int_array *", "p_self"],
+ ["godot_packed_int64_array *", "p_self"],
["const godot_int", "p_idx"],
- ["const godot_int", "p_data"]
+ ["const int64_t", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_int64_array *", "p_self"],
+ ["const int64_t", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int64_array *", "p_self"]
]
},
{
- "name": "godot_packed_int_array_invert",
+ "name": "godot_packed_int64_array_invert",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"]
+ ["godot_packed_int64_array *", "p_self"]
]
},
{
- "name": "godot_packed_int_array_push_back",
+ "name": "godot_packed_int64_array_push_back",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"],
- ["const godot_int", "p_data"]
+ ["godot_packed_int64_array *", "p_self"],
+ ["const int64_t", "p_data"]
]
},
{
- "name": "godot_packed_int_array_remove",
+ "name": "godot_packed_int64_array_remove",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"],
+ ["godot_packed_int64_array *", "p_self"],
["const godot_int", "p_idx"]
]
},
{
- "name": "godot_packed_int_array_resize",
+ "name": "godot_packed_int64_array_resize",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"],
+ ["godot_packed_int64_array *", "p_self"],
["const godot_int", "p_size"]
]
},
{
- "name": "godot_packed_int_array_set",
+ "name": "godot_packed_int64_array_ptr",
+ "return_type": "const int64_t *",
+ "arguments": [
+ ["const godot_packed_int64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_ptrw",
+ "return_type": "int64_t *",
+ "arguments": [
+ ["godot_packed_int64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_set",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"],
+ ["godot_packed_int64_array *", "p_self"],
["const godot_int", "p_idx"],
- ["const godot_int", "p_data"]
+ ["const int64_t", "p_data"]
]
},
{
- "name": "godot_packed_int_array_get",
+ "name": "godot_packed_int64_array_get",
+ "return_type": "int64_t",
+ "arguments": [
+ ["const godot_packed_int64_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_size",
"return_type": "godot_int",
"arguments": [
- ["const godot_packed_int_array *", "p_self"],
+ ["const godot_packed_int64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_int64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_new",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "r_dest"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "r_dest"],
+ ["const godot_packed_float32_array *", "p_src"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_new_with_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "r_dest"],
+ ["const godot_array *", "p_a"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_float32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_append",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
+ ["const float", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_append_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
+ ["const godot_packed_float32_array *", "p_array"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_insert",
+ "return_type": "godot_error",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const float", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
+ ["const float", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_invert",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_push_back",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
+ ["const float", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_remove",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
["const godot_int", "p_idx"]
]
},
{
- "name": "godot_packed_int_array_size",
+ "name": "godot_packed_float32_array_resize",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
+ ["const godot_int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_ptr",
+ "return_type": "const float *",
+ "arguments": [
+ ["const godot_packed_float32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_ptrw",
+ "return_type": "float *",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_set",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float32_array *", "p_self"],
+ ["const godot_int", "p_idx"],
+ ["const float", "p_data"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_get",
+ "return_type": "float",
+ "arguments": [
+ ["const godot_packed_float32_array *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_size",
"return_type": "godot_int",
"arguments": [
- ["const godot_packed_int_array *", "p_self"]
+ ["const godot_packed_float32_array *", "p_self"]
]
},
{
- "name": "godot_packed_int_array_destroy",
+ "name": "godot_packed_float32_array_destroy",
"return_type": "void",
"arguments": [
- ["godot_packed_int_array *", "p_self"]
+ ["godot_packed_float32_array *", "p_self"]
]
},
{
- "name": "godot_packed_real_array_new",
+ "name": "godot_packed_float64_array_new",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "r_dest"]
+ ["godot_packed_float64_array *", "r_dest"]
]
},
{
- "name": "godot_packed_real_array_new_copy",
+ "name": "godot_packed_float64_array_new_copy",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "r_dest"],
- ["const godot_packed_real_array *", "p_src"]
+ ["godot_packed_float64_array *", "r_dest"],
+ ["const godot_packed_float64_array *", "p_src"]
]
},
{
- "name": "godot_packed_real_array_new_with_array",
+ "name": "godot_packed_float64_array_new_with_array",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "r_dest"],
+ ["godot_packed_float64_array *", "r_dest"],
["const godot_array *", "p_a"]
]
},
{
- "name": "godot_packed_real_array_append",
+ "name": "godot_packed_float64_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_float64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_append",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"],
- ["const godot_real", "p_data"]
+ ["godot_packed_float64_array *", "p_self"],
+ ["const double", "p_data"]
]
},
{
- "name": "godot_packed_real_array_append_array",
+ "name": "godot_packed_float64_array_append_array",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"],
- ["const godot_packed_real_array *", "p_array"]
+ ["godot_packed_float64_array *", "p_self"],
+ ["const godot_packed_float64_array *", "p_array"]
]
},
{
- "name": "godot_packed_real_array_insert",
+ "name": "godot_packed_float64_array_insert",
"return_type": "godot_error",
"arguments": [
- ["godot_packed_real_array *", "p_self"],
+ ["godot_packed_float64_array *", "p_self"],
["const godot_int", "p_idx"],
- ["const godot_real", "p_data"]
+ ["const double", "p_data"]
]
},
{
- "name": "godot_packed_real_array_invert",
+ "name": "godot_packed_float64_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_float64_array *", "p_self"],
+ ["const double", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_float64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_invert",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"]
+ ["godot_packed_float64_array *", "p_self"]
]
},
{
- "name": "godot_packed_real_array_push_back",
+ "name": "godot_packed_float64_array_push_back",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"],
- ["const godot_real", "p_data"]
+ ["godot_packed_float64_array *", "p_self"],
+ ["const double", "p_data"]
]
},
{
- "name": "godot_packed_real_array_remove",
+ "name": "godot_packed_float64_array_remove",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"],
+ ["godot_packed_float64_array *", "p_self"],
["const godot_int", "p_idx"]
]
},
{
- "name": "godot_packed_real_array_resize",
+ "name": "godot_packed_float64_array_resize",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"],
+ ["godot_packed_float64_array *", "p_self"],
["const godot_int", "p_size"]
]
},
{
- "name": "godot_packed_real_array_set",
+ "name": "godot_packed_float64_array_ptr",
+ "return_type": "const double *",
+ "arguments": [
+ ["const godot_packed_float64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_ptrw",
+ "return_type": "double *",
+ "arguments": [
+ ["godot_packed_float64_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_set",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"],
+ ["godot_packed_float64_array *", "p_self"],
["const godot_int", "p_idx"],
- ["const godot_real", "p_data"]
+ ["const double", "p_data"]
]
},
{
- "name": "godot_packed_real_array_get",
- "return_type": "godot_real",
+ "name": "godot_packed_float64_array_get",
+ "return_type": "double",
"arguments": [
- ["const godot_packed_real_array *", "p_self"],
+ ["const godot_packed_float64_array *", "p_self"],
["const godot_int", "p_idx"]
]
},
{
- "name": "godot_packed_real_array_size",
+ "name": "godot_packed_float64_array_size",
"return_type": "godot_int",
"arguments": [
- ["const godot_packed_real_array *", "p_self"]
+ ["const godot_packed_float64_array *", "p_self"]
]
},
{
- "name": "godot_packed_real_array_destroy",
+ "name": "godot_packed_float64_array_destroy",
"return_type": "void",
"arguments": [
- ["godot_packed_real_array *", "p_self"]
+ ["godot_packed_float64_array *", "p_self"]
]
},
{
@@ -2010,6 +2376,13 @@
]
},
{
+ "name": "godot_packed_string_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_string_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_string_array_append",
"return_type": "void",
"arguments": [
@@ -2035,6 +2408,21 @@
]
},
{
+ "name": "godot_packed_string_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_string_array *", "p_self"],
+ ["const godot_string *", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_string_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_string_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_string_array_invert",
"return_type": "void",
"arguments": [
@@ -2066,6 +2454,20 @@
]
},
{
+ "name": "godot_packed_string_array_ptr",
+ "return_type": "const godot_string *",
+ "arguments": [
+ ["const godot_packed_string_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_string_array_ptrw",
+ "return_type": "godot_string *",
+ "arguments": [
+ ["godot_packed_string_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_string_array_set",
"return_type": "void",
"arguments": [
@@ -2120,6 +2522,13 @@
]
},
{
+ "name": "godot_packed_vector2_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_vector2_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_vector2_array_append",
"return_type": "void",
"arguments": [
@@ -2145,6 +2554,21 @@
]
},
{
+ "name": "godot_packed_vector2_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_vector2_array *", "p_self"],
+ ["const godot_vector2 *", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector2_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_vector2_array_invert",
"return_type": "void",
"arguments": [
@@ -2176,6 +2600,20 @@
]
},
{
+ "name": "godot_packed_vector2_array_ptr",
+ "return_type": "const godot_vector2 *",
+ "arguments": [
+ ["const godot_packed_vector2_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2_array_ptrw",
+ "return_type": "godot_vector2 *",
+ "arguments": [
+ ["godot_packed_vector2_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_vector2_array_set",
"return_type": "void",
"arguments": [
@@ -2230,6 +2668,13 @@
]
},
{
+ "name": "godot_packed_vector3_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_vector3_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_vector3_array_append",
"return_type": "void",
"arguments": [
@@ -2255,6 +2700,21 @@
]
},
{
+ "name": "godot_packed_vector3_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_vector3_array *", "p_self"],
+ ["const godot_vector3 *", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_vector3_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_vector3_array_invert",
"return_type": "void",
"arguments": [
@@ -2286,6 +2746,20 @@
]
},
{
+ "name": "godot_packed_vector3_array_ptr",
+ "return_type": "const godot_vector3 *",
+ "arguments": [
+ ["const godot_packed_vector3_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3_array_ptrw",
+ "return_type": "godot_vector3 *",
+ "arguments": [
+ ["godot_packed_vector3_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_vector3_array_set",
"return_type": "void",
"arguments": [
@@ -2340,6 +2814,13 @@
]
},
{
+ "name": "godot_packed_color_array_empty",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_packed_color_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_color_array_append",
"return_type": "void",
"arguments": [
@@ -2365,6 +2846,21 @@
]
},
{
+ "name": "godot_packed_color_array_has",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_packed_color_array *", "p_self"],
+ ["const godot_color *", "p_value"]
+ ]
+ },
+ {
+ "name": "godot_packed_color_array_sort",
+ "return_type": "void",
+ "arguments": [
+ ["godot_packed_color_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_color_array_invert",
"return_type": "void",
"arguments": [
@@ -2396,6 +2892,20 @@
]
},
{
+ "name": "godot_packed_color_array_ptr",
+ "return_type": "const godot_color *",
+ "arguments": [
+ ["const godot_packed_color_array *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_packed_color_array_ptrw",
+ "return_type": "godot_color *",
+ "arguments": [
+ ["godot_packed_color_array *", "p_self"]
+ ]
+ },
+ {
"name": "godot_packed_color_array_set",
"return_type": "void",
"arguments": [
@@ -2427,1090 +2937,2198 @@
]
},
{
- "name": "godot_array_new",
+ "name": "godot_plane_new_with_reals",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"]
+ ["godot_plane *", "r_dest"],
+ ["const godot_real", "p_a"],
+ ["const godot_real", "p_b"],
+ ["const godot_real", "p_c"],
+ ["const godot_real", "p_d"]
]
},
{
- "name": "godot_array_new_copy",
+ "name": "godot_plane_new_with_vectors",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_array *", "p_src"]
+ ["godot_plane *", "r_dest"],
+ ["const godot_vector3 *", "p_v1"],
+ ["const godot_vector3 *", "p_v2"],
+ ["const godot_vector3 *", "p_v3"]
]
},
{
- "name": "godot_array_new_packed_color_array",
+ "name": "godot_plane_new_with_normal",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_color_array *", "p_pca"]
+ ["godot_plane *", "r_dest"],
+ ["const godot_vector3 *", "p_normal"],
+ ["const godot_real", "p_d"]
]
},
{
- "name": "godot_array_new_packed_vector3_array",
+ "name": "godot_plane_as_string",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_plane *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_plane_normalized",
+ "return_type": "godot_plane",
+ "arguments": [
+ ["const godot_plane *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_plane_center",
+ "return_type": "godot_vector3",
+ "arguments": [
+ ["const godot_plane *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_plane_is_point_over",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["const godot_vector3 *", "p_point"]
+ ]
+ },
+ {
+ "name": "godot_plane_distance_to",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["const godot_vector3 *", "p_point"]
+ ]
+ },
+ {
+ "name": "godot_plane_has_point",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["const godot_vector3 *", "p_point"],
+ ["const godot_real", "p_epsilon"]
+ ]
+ },
+ {
+ "name": "godot_plane_project",
+ "return_type": "godot_vector3",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["const godot_vector3 *", "p_point"]
+ ]
+ },
+ {
+ "name": "godot_plane_intersect_3",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["godot_vector3 *", "r_dest"],
+ ["const godot_plane *", "p_b"],
+ ["const godot_plane *", "p_c"]
+ ]
+ },
+ {
+ "name": "godot_plane_intersects_ray",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["godot_vector3 *", "r_dest"],
+ ["const godot_vector3 *", "p_from"],
+ ["const godot_vector3 *", "p_dir"]
+ ]
+ },
+ {
+ "name": "godot_plane_intersects_segment",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["godot_vector3 *", "r_dest"],
+ ["const godot_vector3 *", "p_begin"],
+ ["const godot_vector3 *", "p_end"]
+ ]
+ },
+ {
+ "name": "godot_plane_operator_neg",
+ "return_type": "godot_plane",
+ "arguments": [
+ ["const godot_plane *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_plane_operator_equal",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_plane *", "p_self"],
+ ["const godot_plane *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_plane_set_normal",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_vector3_array *", "p_pv3a"]
+ ["godot_plane *", "p_self"],
+ ["const godot_vector3 *", "p_normal"]
]
},
{
- "name": "godot_array_new_packed_vector2_array",
+ "name": "godot_plane_get_normal",
+ "return_type": "godot_vector3",
+ "arguments": [
+ ["const godot_plane *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_plane_get_d",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_plane *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_plane_set_d",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_vector2_array *", "p_pv2a"]
+ ["godot_plane *", "p_self"],
+ ["const godot_real", "p_d"]
]
},
{
- "name": "godot_array_new_packed_string_array",
+ "name": "godot_quat_new",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_string_array *", "p_psa"]
+ ["godot_quat *", "r_dest"],
+ ["const godot_real", "p_x"],
+ ["const godot_real", "p_y"],
+ ["const godot_real", "p_z"],
+ ["const godot_real", "p_w"]
]
},
{
- "name": "godot_array_new_packed_real_array",
+ "name": "godot_quat_new_with_axis_angle",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_real_array *", "p_pra"]
+ ["godot_quat *", "r_dest"],
+ ["const godot_vector3 *", "p_axis"],
+ ["const godot_real", "p_angle"]
]
},
{
- "name": "godot_array_new_packed_int_array",
+ "name": "godot_quat_new_with_basis",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_int_array *", "p_pia"]
+ ["godot_quat *", "r_dest"],
+ ["const godot_basis *", "p_basis"]
]
},
{
- "name": "godot_array_new_packed_byte_array",
+ "name": "godot_quat_new_with_euler",
"return_type": "void",
"arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_byte_array *", "p_pba"]
+ ["godot_quat *", "r_dest"],
+ ["const godot_vector3 *", "p_euler"]
]
},
{
- "name": "godot_array_set",
+ "name": "godot_quat_get_x",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_quat *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_set_x",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_variant *", "p_value"]
+ ["godot_quat *", "p_self"],
+ ["const godot_real", "val"]
]
},
{
- "name": "godot_array_get",
- "return_type": "godot_variant",
+ "name": "godot_quat_get_y",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["const godot_quat *", "p_self"]
]
},
{
- "name": "godot_array_operator_index",
- "return_type": "godot_variant *",
+ "name": "godot_quat_set_y",
+ "return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["godot_quat *", "p_self"],
+ ["const godot_real", "val"]
]
},
{
- "name": "godot_array_operator_index_const",
- "return_type": "const godot_variant *",
+ "name": "godot_quat_get_z",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["const godot_quat *", "p_self"]
]
},
{
- "name": "godot_array_append",
+ "name": "godot_quat_set_z",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
+ ["godot_quat *", "p_self"],
+ ["const godot_real", "val"]
]
},
{
- "name": "godot_array_clear",
+ "name": "godot_quat_get_w",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_quat *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_set_w",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"]
+ ["godot_quat *", "p_self"],
+ ["const godot_real", "val"]
]
},
{
- "name": "godot_array_count",
- "return_type": "godot_int",
+ "name": "godot_quat_set_axis_angle",
+ "return_type": "void",
"arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
+ ["godot_quat *", "p_self"],
+ ["const godot_vector3 *", "p_axis"],
+ ["const godot_real", "p_angle"]
]
},
{
- "name": "godot_array_empty",
+ "name": "godot_quat_as_string",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_quat *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_length",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_quat *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_length_squared",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_quat *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_normalized",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_quat *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_quat_is_normalized",
"return_type": "godot_bool",
"arguments": [
- ["const godot_array *", "p_self"]
+ ["const godot_quat *", "p_self"]
]
},
{
- "name": "godot_array_erase",
- "return_type": "void",
+ "name": "godot_quat_inverse",
+ "return_type": "godot_quat",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
+ ["const godot_quat *", "p_self"]
]
},
{
- "name": "godot_array_front",
- "return_type": "godot_variant",
+ "name": "godot_quat_dot",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_array *", "p_self"]
+ ["const godot_quat *", "p_self"],
+ ["const godot_quat *", "p_b"]
]
},
{
- "name": "godot_array_back",
- "return_type": "godot_variant",
+ "name": "godot_quat_xform",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_array *", "p_self"]
+ ["const godot_quat *", "p_self"],
+ ["const godot_vector3 *", "p_v"]
]
},
{
- "name": "godot_array_find",
- "return_type": "godot_int",
+ "name": "godot_quat_slerp",
+ "return_type": "godot_quat",
"arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_what"],
- ["const godot_int", "p_from"]
+ ["const godot_quat *", "p_self"],
+ ["const godot_quat *", "p_b"],
+ ["const godot_real", "p_t"]
]
},
{
- "name": "godot_array_find_last",
- "return_type": "godot_int",
+ "name": "godot_quat_slerpni",
+ "return_type": "godot_quat",
"arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_what"]
+ ["const godot_quat *", "p_self"],
+ ["const godot_quat *", "p_b"],
+ ["const godot_real", "p_t"]
]
},
{
- "name": "godot_array_has",
+ "name": "godot_quat_cubic_slerp",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_quat *", "p_self"],
+ ["const godot_quat *", "p_b"],
+ ["const godot_quat *", "p_pre_a"],
+ ["const godot_quat *", "p_post_b"],
+ ["const godot_real", "p_t"]
+ ]
+ },
+ {
+ "name": "godot_quat_operator_multiply",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_quat *", "p_self"],
+ ["const godot_real", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_quat_operator_add",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_quat *", "p_self"],
+ ["const godot_quat *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_quat_operator_subtract",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_quat *", "p_self"],
+ ["const godot_quat *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_quat_operator_divide",
+ "return_type": "godot_quat",
+ "arguments": [
+ ["const godot_quat *", "p_self"],
+ ["const godot_real", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_quat_operator_equal",
"return_type": "godot_bool",
"arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
+ ["const godot_quat *", "p_self"],
+ ["const godot_quat *", "p_b"]
]
},
{
- "name": "godot_array_hash",
- "return_type": "godot_int",
+ "name": "godot_quat_operator_neg",
+ "return_type": "godot_quat",
"arguments": [
- ["const godot_array *", "p_self"]
+ ["const godot_quat *", "p_self"]
]
},
{
- "name": "godot_array_insert",
+ "name": "godot_rect2_new_with_position_and_size",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_pos"],
- ["const godot_variant *", "p_value"]
+ ["godot_rect2 *", "r_dest"],
+ ["const godot_vector2 *", "p_pos"],
+ ["const godot_vector2 *", "p_size"]
]
},
{
- "name": "godot_array_invert",
+ "name": "godot_rect2_new",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"]
+ ["godot_rect2 *", "r_dest"],
+ ["const godot_real", "p_x"],
+ ["const godot_real", "p_y"],
+ ["const godot_real", "p_width"],
+ ["const godot_real", "p_height"]
]
},
{
- "name": "godot_array_pop_back",
- "return_type": "godot_variant",
+ "name": "godot_rect2_as_rect2i",
+ "return_type": "godot_rect2i",
"arguments": [
- ["godot_array *", "p_self"]
+ ["const godot_rect2 *", "p_self"]
]
},
{
- "name": "godot_array_pop_front",
- "return_type": "godot_variant",
+ "name": "godot_rect2_as_string",
+ "return_type": "godot_string",
"arguments": [
- ["godot_array *", "p_self"]
+ ["const godot_rect2 *", "p_self"]
]
},
{
- "name": "godot_array_push_back",
+ "name": "godot_rect2_get_area",
+ "return_type": "godot_real",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2_grow_individual",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_real", "p_left"],
+ ["const godot_real", "p_top"],
+ ["const godot_real", "p_right"],
+ ["const godot_real", "p_bottom"]
+ ]
+ },
+ {
+ "name": "godot_rect2_grow_margin",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_int", "p_margin"],
+ ["const godot_real", "p_by"]
+ ]
+ },
+ {
+ "name": "godot_rect2_abs",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2_intersects",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_rect2 *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_rect2_encloses",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_rect2 *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_rect2_has_no_area",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2_clip",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_rect2 *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_rect2_merge",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_rect2 *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_rect2_has_point",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_vector2 *", "p_point"]
+ ]
+ },
+ {
+ "name": "godot_rect2_grow",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_real", "p_by"]
+ ]
+ },
+ {
+ "name": "godot_rect2_expand",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"]
+ ]
+ },
+ {
+ "name": "godot_rect2_operator_equal",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"],
+ ["const godot_rect2 *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_rect2_get_position",
+ "return_type": "godot_vector2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2_get_size",
+ "return_type": "godot_vector2",
+ "arguments": [
+ ["const godot_rect2 *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2_set_position",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
+ ["godot_rect2 *", "p_self"],
+ ["const godot_vector2 *", "p_pos"]
]
},
{
- "name": "godot_array_push_front",
+ "name": "godot_rect2_set_size",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
+ ["godot_rect2 *", "p_self"],
+ ["const godot_vector2 *", "p_size"]
]
},
{
- "name": "godot_array_remove",
+ "name": "godot_rect2i_new_with_position_and_size",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["godot_rect2i *", "r_dest"],
+ ["const godot_vector2i *", "p_pos"],
+ ["const godot_vector2i *", "p_size"]
]
},
{
- "name": "godot_array_resize",
+ "name": "godot_rect2i_new",
"return_type": "void",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_size"]
+ ["godot_rect2i *", "r_dest"],
+ ["const godot_int", "p_x"],
+ ["const godot_int", "p_y"],
+ ["const godot_int", "p_width"],
+ ["const godot_int", "p_height"]
]
},
{
- "name": "godot_array_rfind",
- "return_type": "godot_int",
+ "name": "godot_rect2i_as_string",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_what"],
- ["const godot_int", "p_from"]
+ ["const godot_rect2i *", "p_self"]
]
},
{
- "name": "godot_array_size",
+ "name": "godot_rect2i_as_rect2",
+ "return_type": "godot_rect2",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_get_area",
"return_type": "godot_int",
"arguments": [
- ["const godot_array *", "p_self"]
+ ["const godot_rect2i *", "p_self"]
]
},
{
- "name": "godot_array_sort",
- "return_type": "void",
+ "name": "godot_rect2i_intersects",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_array *", "p_self"]
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_rect2i *", "p_b"]
]
},
{
- "name": "godot_array_sort_custom",
- "return_type": "void",
+ "name": "godot_rect2i_encloses",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_array *", "p_self"],
- ["godot_object *", "p_obj"],
- ["const godot_string *", "p_func"]
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_rect2i *", "p_b"]
]
},
{
- "name": "godot_array_bsearch",
- "return_type": "godot_int",
+ "name": "godot_rect2i_has_no_area",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"],
- ["const godot_bool", "p_before"]
+ ["const godot_rect2i *", "p_self"]
]
},
{
- "name": "godot_array_bsearch_custom",
- "return_type": "godot_int",
+ "name": "godot_rect2i_clip",
+ "return_type": "godot_rect2i",
"arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"],
- ["godot_object *", "p_obj"],
- ["const godot_string *", "p_func"],
- ["const godot_bool", "p_before"]
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_rect2i *", "p_b"]
]
},
{
- "name": "godot_array_destroy",
- "return_type": "void",
+ "name": "godot_rect2i_merge",
+ "return_type": "godot_rect2i",
"arguments": [
- ["godot_array *", "p_self"]
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_rect2i *", "p_b"]
]
},
{
- "name": "godot_dictionary_new",
- "return_type": "void",
+ "name": "godot_rect2i_has_point",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_dictionary *", "r_dest"]
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_vector2i *", "p_point"]
]
},
{
- "name": "godot_dictionary_new_copy",
+ "name": "godot_rect2i_grow",
+ "return_type": "godot_rect2i",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_int", "p_by"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_grow_individual",
+ "return_type": "godot_rect2i",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_int", "p_left"],
+ ["const godot_int", "p_top"],
+ ["const godot_int", "p_right"],
+ ["const godot_int", "p_bottom"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_grow_margin",
+ "return_type": "godot_rect2i",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_int", "p_margin"],
+ ["const godot_int", "p_by"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_abs",
+ "return_type": "godot_rect2i",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_expand",
+ "return_type": "godot_rect2i",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_vector2i *", "p_to"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_operator_equal",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"],
+ ["const godot_rect2i *", "p_b"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_get_position",
+ "return_type": "godot_vector2i",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_get_size",
+ "return_type": "godot_vector2i",
+ "arguments": [
+ ["const godot_rect2i *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_rect2i_set_position",
"return_type": "void",
"arguments": [
- ["godot_dictionary *", "r_dest"],
- ["const godot_dictionary *", "p_src"]
+ ["godot_rect2i *", "p_self"],
+ ["const godot_vector2i *", "p_pos"]
]
},
{
- "name": "godot_dictionary_destroy",
+ "name": "godot_rect2i_set_size",
"return_type": "void",
"arguments": [
- ["godot_dictionary *", "p_self"]
+ ["godot_rect2i *", "p_self"],
+ ["const godot_vector2i *", "p_size"]
]
},
{
- "name": "godot_dictionary_size",
- "return_type": "godot_int",
+ "name": "godot_rid_new",
+ "return_type": "void",
"arguments": [
- ["const godot_dictionary *", "p_self"]
+ ["godot_rid *", "r_dest"]
]
},
{
- "name": "godot_dictionary_empty",
- "return_type": "godot_bool",
+ "name": "godot_rid_get_id",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_dictionary *", "p_self"]
+ ["const godot_rid *", "p_self"]
]
},
{
- "name": "godot_dictionary_clear",
+ "name": "godot_rid_new_with_resource",
"return_type": "void",
"arguments": [
- ["godot_dictionary *", "p_self"]
+ ["godot_rid *", "r_dest"],
+ ["const godot_object *", "p_from"]
]
},
{
- "name": "godot_dictionary_has",
+ "name": "godot_rid_operator_equal",
"return_type": "godot_bool",
"arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
+ ["const godot_rid *", "p_self"],
+ ["const godot_rid *", "p_b"]
]
},
{
- "name": "godot_dictionary_has_all",
+ "name": "godot_rid_operator_less",
"return_type": "godot_bool",
"arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_array *", "p_keys"]
+ ["const godot_rid *", "p_self"],
+ ["const godot_rid *", "p_b"]
]
},
{
- "name": "godot_dictionary_erase",
+ "name": "godot_char_string_length",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_char_string *", "p_cs"]
+ ]
+ },
+ {
+ "name": "godot_char_string_get_data",
+ "return_type": "const char *",
+ "arguments": [
+ ["const godot_char_string *", "p_cs"]
+ ]
+ },
+ {
+ "name": "godot_char_string_destroy",
"return_type": "void",
"arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
+ ["godot_char_string *", "p_cs"]
]
},
{
- "name": "godot_dictionary_hash",
+ "name": "godot_char16_string_length",
"return_type": "godot_int",
"arguments": [
- ["const godot_dictionary *", "p_self"]
+ ["const godot_char16_string *", "p_cs"]
]
},
{
- "name": "godot_dictionary_keys",
- "return_type": "godot_array",
+ "name": "godot_char16_string_get_data",
+ "return_type": "const char16_t *",
"arguments": [
- ["const godot_dictionary *", "p_self"]
+ ["const godot_char16_string *", "p_cs"]
]
},
{
- "name": "godot_dictionary_values",
- "return_type": "godot_array",
+ "name": "godot_char16_string_destroy",
+ "return_type": "void",
"arguments": [
- ["const godot_dictionary *", "p_self"]
+ ["godot_char16_string *", "p_cs"]
]
},
{
- "name": "godot_dictionary_get",
- "return_type": "godot_variant",
+ "name": "godot_string_new",
+ "return_type": "void",
"arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
+ ["godot_string *", "r_dest"]
]
},
{
- "name": "godot_dictionary_set",
+ "name": "godot_string_new_copy",
"return_type": "void",
"arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"],
- ["const godot_variant *", "p_value"]
+ ["godot_string *", "r_dest"],
+ ["const godot_string *", "p_src"]
]
},
{
- "name": "godot_dictionary_operator_index",
- "return_type": "godot_variant *",
+ "name": "godot_string_new_with_latin1_chars",
+ "return_type": "void",
"arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"]
]
},
{
- "name": "godot_dictionary_operator_index_const",
- "return_type": "const godot_variant *",
+ "name": "godot_string_new_with_utf8_chars",
+ "return_type": "void",
"arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"]
]
},
{
- "name": "godot_dictionary_next",
- "return_type": "godot_variant *",
+ "name": "godot_string_new_with_utf16_chars",
+ "return_type": "void",
"arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
+ ["godot_string *", "r_dest"],
+ ["const char16_t *", "p_contents"]
]
},
{
- "name": "godot_dictionary_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_string_new_with_utf32_chars",
+ "return_type": "void",
"arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_dictionary *", "p_b"]
+ ["godot_string *", "r_dest"],
+ ["const char32_t *", "p_contents"]
]
},
{
- "name": "godot_dictionary_to_json",
- "return_type": "godot_string",
+ "name": "godot_string_new_with_wide_chars",
+ "return_type": "void",
"arguments": [
- ["const godot_dictionary *", "p_self"]
+ ["godot_string *", "r_dest"],
+ ["const wchar_t *", "p_contents"]
]
},
{
- "name": "godot_node_path_new",
+ "name": "godot_string_new_with_latin1_chars_and_len",
"return_type": "void",
"arguments": [
- ["godot_node_path *", "r_dest"],
- ["const godot_string *", "p_from"]
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"],
+ ["const int", "p_size"]
]
},
{
- "name": "godot_node_path_new_copy",
+ "name": "godot_string_new_with_utf8_chars_and_len",
"return_type": "void",
"arguments": [
- ["godot_node_path *", "r_dest"],
- ["const godot_node_path *", "p_src"]
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"],
+ ["const int", "p_size"]
]
},
{
- "name": "godot_node_path_destroy",
+ "name": "godot_string_new_with_utf16_chars_and_len",
"return_type": "void",
"arguments": [
- ["godot_node_path *", "p_self"]
+ ["godot_string *", "r_dest"],
+ ["const char16_t *", "p_contents"],
+ ["const int", "p_size"]
]
},
{
- "name": "godot_node_path_as_string",
- "return_type": "godot_string",
+ "name": "godot_string_new_with_utf32_chars_and_len",
+ "return_type": "void",
"arguments": [
- ["const godot_node_path *", "p_self"]
+ ["godot_string *", "r_dest"],
+ ["const char32_t *", "p_contents"],
+ ["const int", "p_size"]
]
},
{
- "name": "godot_node_path_is_absolute",
+ "name": "godot_string_new_with_wide_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const wchar_t *", "p_contents"],
+ ["const int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_string_operator_index",
+ "return_type": "const godot_char_type *",
+ "arguments": [
+ ["godot_string *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_string_operator_index_const",
+ "return_type": "godot_char_type",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_string_get_data",
+ "return_type": "const godot_char_type *",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_operator_equal",
"return_type": "godot_bool",
"arguments": [
- ["const godot_node_path *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_b"]
]
},
{
- "name": "godot_node_path_get_name_count",
- "return_type": "godot_int",
+ "name": "godot_string_operator_less",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_node_path *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_b"]
]
},
{
- "name": "godot_node_path_get_name",
+ "name": "godot_string_operator_plus",
"return_type": "godot_string",
"arguments": [
- ["const godot_node_path *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_b"]
]
},
{
- "name": "godot_node_path_get_subname_count",
+ "name": "godot_string_count",
"return_type": "godot_int",
"arguments": [
- ["const godot_node_path *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"],
+ ["godot_int", "p_from"],
+ ["godot_int", "p_to"]
]
},
{
- "name": "godot_node_path_get_subname",
+ "name": "godot_string_countn",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"],
+ ["godot_int", "p_from"],
+ ["godot_int", "p_to"]
+ ]
+ },
+ {
+ "name": "godot_string_dedent",
"return_type": "godot_string",
"arguments": [
- ["const godot_node_path *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_node_path_get_concatenated_subnames",
+ "name": "godot_string_length",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_casecmp_to",
+ "return_type": "signed char",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_str"]
+ ]
+ },
+ {
+ "name": "godot_string_nocasecmp_to",
+ "return_type": "signed char",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_str"]
+ ]
+ },
+ {
+ "name": "godot_string_naturalnocasecmp_to",
+ "return_type": "signed char",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_str"]
+ ]
+ },
+ {
+ "name": "godot_string_begins_with",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_string"]
+ ]
+ },
+ {
+ "name": "godot_string_begins_with_char_array",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const char *", "p_char_array"]
+ ]
+ },
+ {
+ "name": "godot_string_bigrams",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_chr",
"return_type": "godot_string",
"arguments": [
- ["const godot_node_path *", "p_self"]
+ ["godot_char_type", "p_character"]
]
},
{
- "name": "godot_node_path_is_empty",
+ "name": "godot_string_ends_with",
"return_type": "godot_bool",
"arguments": [
- ["const godot_node_path *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_string"]
]
},
{
- "name": "godot_node_path_operator_equal",
+ "name": "godot_string_ends_with_char_array",
"return_type": "godot_bool",
"arguments": [
- ["const godot_node_path *", "p_self"],
- ["const godot_node_path *", "p_b"]
+ ["const godot_string *", "p_self"],
+ ["const char *", "p_char_array"]
]
},
{
- "name": "godot_plane_new_with_reals",
- "return_type": "void",
+ "name": "godot_string_find",
+ "return_type": "godot_int",
"arguments": [
- ["godot_plane *", "r_dest"],
- ["const godot_real", "p_a"],
- ["const godot_real", "p_b"],
- ["const godot_real", "p_c"],
- ["const godot_real", "p_d"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"]
]
},
{
- "name": "godot_plane_new_with_vectors",
- "return_type": "void",
+ "name": "godot_string_find_from",
+ "return_type": "godot_int",
"arguments": [
- ["godot_plane *", "r_dest"],
- ["const godot_vector3 *", "p_v1"],
- ["const godot_vector3 *", "p_v2"],
- ["const godot_vector3 *", "p_v3"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"],
+ ["godot_int", "p_from"]
]
},
{
- "name": "godot_plane_new_with_normal",
- "return_type": "void",
+ "name": "godot_string_findmk",
+ "return_type": "godot_int",
"arguments": [
- ["godot_plane *", "r_dest"],
- ["const godot_vector3 *", "p_normal"],
- ["const godot_real", "p_d"]
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_keys"]
]
},
{
- "name": "godot_plane_as_string",
+ "name": "godot_string_findmk_from",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_keys"],
+ ["godot_int", "p_from"]
+ ]
+ },
+ {
+ "name": "godot_string_findmk_from_in_place",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_keys"],
+ ["godot_int", "p_from"],
+ ["godot_int *", "r_key"]
+ ]
+ },
+ {
+ "name": "godot_string_findn",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"]
+ ]
+ },
+ {
+ "name": "godot_string_findn_from",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"],
+ ["godot_int", "p_from"]
+ ]
+ },
+ {
+ "name": "godot_string_format",
"return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_variant *", "p_values"]
]
},
{
- "name": "godot_plane_normalized",
- "return_type": "godot_plane",
+ "name": "godot_string_format_with_custom_placeholder",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_variant *", "p_values"],
+ ["const char *", "p_placeholder"]
]
},
{
- "name": "godot_plane_center",
- "return_type": "godot_vector3",
+ "name": "godot_string_hex_encode_buffer",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"]
+ ["const uint8_t *", "p_buffer"],
+ ["godot_int", "p_len"]
]
},
{
- "name": "godot_plane_get_any_point",
- "return_type": "godot_vector3",
+ "name": "godot_string_insert",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_at_pos"],
+ ["const godot_string *", "p_string"]
]
},
{
- "name": "godot_plane_is_point_over",
+ "name": "godot_string_is_numeric",
"return_type": "godot_bool",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_plane_distance_to",
- "return_type": "godot_real",
+ "name": "godot_string_is_subsequence_of",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_string"]
]
},
{
- "name": "godot_plane_has_point",
+ "name": "godot_string_is_subsequence_ofi",
"return_type": "godot_bool",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"],
- ["const godot_real", "p_epsilon"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_string"]
]
},
{
- "name": "godot_plane_project",
- "return_type": "godot_vector3",
+ "name": "godot_string_lpad",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"]
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_min_length"]
]
},
{
- "name": "godot_plane_intersect_3",
- "return_type": "godot_bool",
+ "name": "godot_string_lpad_with_custom_character",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["godot_vector3 *", "r_dest"],
- ["const godot_plane *", "p_b"],
- ["const godot_plane *", "p_c"]
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_min_length"],
+ ["const godot_string *", "p_character"]
]
},
{
- "name": "godot_plane_intersects_ray",
+ "name": "godot_string_match",
"return_type": "godot_bool",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["godot_vector3 *", "r_dest"],
- ["const godot_vector3 *", "p_from"],
- ["const godot_vector3 *", "p_dir"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_wildcard"]
]
},
{
- "name": "godot_plane_intersects_segment",
+ "name": "godot_string_matchn",
"return_type": "godot_bool",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["godot_vector3 *", "r_dest"],
- ["const godot_vector3 *", "p_begin"],
- ["const godot_vector3 *", "p_end"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_wildcard"]
]
},
{
- "name": "godot_plane_operator_neg",
- "return_type": "godot_plane",
+ "name": "godot_string_md5",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"]
+ ["const uint8_t *", "p_md5"]
]
},
{
- "name": "godot_plane_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_string_num",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_plane *", "p_b"]
+ ["double", "p_num"]
]
},
{
- "name": "godot_plane_set_normal",
- "return_type": "void",
+ "name": "godot_string_num_int64",
+ "return_type": "godot_string",
"arguments": [
- ["godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_normal"]
+ ["int64_t", "p_num"],
+ ["godot_int", "p_base"]
]
},
{
- "name": "godot_plane_get_normal",
- "return_type": "godot_vector3",
+ "name": "godot_string_num_int64_capitalized",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"]
+ ["int64_t", "p_num"],
+ ["godot_int", "p_base"],
+ ["godot_bool", "p_capitalize_hex"]
]
},
{
- "name": "godot_plane_get_d",
- "return_type": "godot_real",
+ "name": "godot_string_num_real",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_plane *", "p_self"]
+ ["double", "p_num"]
]
},
{
- "name": "godot_plane_set_d",
- "return_type": "void",
+ "name": "godot_string_num_scientific",
+ "return_type": "godot_string",
"arguments": [
- ["godot_plane *", "p_self"],
- ["const godot_real", "p_d"]
+ ["double", "p_num"]
]
},
{
- "name": "godot_rect2_new_with_position_and_size",
- "return_type": "void",
+ "name": "godot_string_num_with_decimals",
+ "return_type": "godot_string",
"arguments": [
- ["godot_rect2 *", "r_dest"],
- ["const godot_vector2 *", "p_pos"],
- ["const godot_vector2 *", "p_size"]
+ ["double", "p_num"],
+ ["godot_int", "p_decimals"]
]
},
{
- "name": "godot_rect2_new",
- "return_type": "void",
+ "name": "godot_string_pad_decimals",
+ "return_type": "godot_string",
"arguments": [
- ["godot_rect2 *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"],
- ["const godot_real", "p_width"],
- ["const godot_real", "p_height"]
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_digits"]
]
},
{
- "name": "godot_rect2_as_string",
+ "name": "godot_string_pad_zeros",
"return_type": "godot_string",
"arguments": [
- ["const godot_rect2 *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_digits"]
]
},
{
- "name": "godot_rect2_get_area",
+ "name": "godot_string_replace_first",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_key"],
+ ["const godot_string *", "p_with"]
+ ]
+ },
+ {
+ "name": "godot_string_replace",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_key"],
+ ["const godot_string *", "p_with"]
+ ]
+ },
+ {
+ "name": "godot_string_replacen",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_key"],
+ ["const godot_string *", "p_with"]
+ ]
+ },
+ {
+ "name": "godot_string_rfind",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"]
+ ]
+ },
+ {
+ "name": "godot_string_rfindn",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"]
+ ]
+ },
+ {
+ "name": "godot_string_rfind_from",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"],
+ ["godot_int", "p_from"]
+ ]
+ },
+ {
+ "name": "godot_string_rfindn_from",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_what"],
+ ["godot_int", "p_from"]
+ ]
+ },
+ {
+ "name": "godot_string_rpad",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_min_length"]
+ ]
+ },
+ {
+ "name": "godot_string_rpad_with_custom_character",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_min_length"],
+ ["const godot_string *", "p_character"]
+ ]
+ },
+ {
+ "name": "godot_string_similarity",
"return_type": "godot_real",
"arguments": [
- ["const godot_rect2 *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_string"]
]
},
{
- "name": "godot_rect2_intersects",
- "return_type": "godot_bool",
+ "name": "godot_string_sprintf",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
+ ["const godot_string *", "p_self"],
+ ["const godot_array *", "p_values"],
+ ["godot_bool *", "p_error"]
]
},
{
- "name": "godot_rect2_encloses",
- "return_type": "godot_bool",
+ "name": "godot_string_substr",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_from"],
+ ["godot_int", "p_chars"]
]
},
{
- "name": "godot_rect2_has_no_area",
- "return_type": "godot_bool",
+ "name": "godot_string_to_int",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_rect2 *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_rect2_clip",
- "return_type": "godot_rect2",
+ "name": "godot_string_to_float",
+ "return_type": "double",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_rect2_merge",
- "return_type": "godot_rect2",
+ "name": "godot_string_camelcase_to_underscore",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_rect2_has_point",
- "return_type": "godot_bool",
+ "name": "godot_string_camelcase_to_underscore_lowercased",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_point"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_rect2_grow",
- "return_type": "godot_rect2",
+ "name": "godot_string_capitalize",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_real", "p_by"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_rect2_expand",
- "return_type": "godot_rect2",
+ "name": "godot_string_char_to_float",
+ "return_type": "double",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
+ ["const char *", "p_what"]
]
},
{
- "name": "godot_rect2_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_string_wchar_to_float",
+ "return_type": "double",
"arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
+ ["const wchar_t *", "p_str"],
+ ["const wchar_t **", "r_end"]
]
},
{
- "name": "godot_rect2_get_position",
- "return_type": "godot_vector2",
+ "name": "godot_string_char_to_int",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_rect2 *", "p_self"]
+ ["const char *", "p_what"]
]
},
{
- "name": "godot_rect2_get_size",
- "return_type": "godot_vector2",
+ "name": "godot_string_wchar_to_int",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_rect2 *", "p_self"]
+ ["const wchar_t *", "p_str"]
]
},
{
- "name": "godot_rect2_set_position",
- "return_type": "void",
+ "name": "godot_string_char_to_int_with_len",
+ "return_type": "godot_int",
"arguments": [
- ["godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_pos"]
+ ["const char *", "p_what"],
+ ["godot_int", "p_len"]
]
},
{
- "name": "godot_rect2_set_size",
- "return_type": "void",
+ "name": "godot_string_wchar_to_int_with_len",
+ "return_type": "godot_int",
"arguments": [
- ["godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_size"]
+ ["const wchar_t *", "p_str"],
+ ["int", "p_len"]
]
},
{
- "name": "godot_aabb_new",
- "return_type": "void",
+ "name": "godot_string_hex_to_int",
+ "return_type": "godot_int",
"arguments": [
- ["godot_aabb *", "r_dest"],
- ["const godot_vector3 *", "p_pos"],
- ["const godot_vector3 *", "p_size"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_get_position",
- "return_type": "godot_vector3",
+ "name": "godot_string_hex_to_int_with_prefix",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_set_position",
- "return_type": "void",
+ "name": "godot_string_get_slice_count",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_v"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
]
},
{
- "name": "godot_aabb_get_size",
- "return_type": "godot_vector3",
+ "name": "godot_string_get_slice",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"],
+ ["godot_int", "p_slice"]
]
},
{
- "name": "godot_aabb_set_size",
- "return_type": "void",
+ "name": "godot_string_get_slicec",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_v"]
+ ["const godot_string *", "p_self"],
+ ["godot_char_type", "p_splitter"],
+ ["godot_int", "p_slice"]
]
},
{
- "name": "godot_aabb_as_string",
+ "name": "godot_string_split",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_split_allow_empty",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_split_with_maxsplit",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"],
+ ["const godot_bool", "p_allow_empty"],
+ ["const godot_int", "p_maxsplit"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit_allow_empty",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit_with_maxsplit",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"],
+ ["const godot_bool", "p_allow_empty"],
+ ["const godot_int", "p_maxsplit"]
+ ]
+ },
+ {
+ "name": "godot_string_split_floats",
+ "return_type": "godot_packed_float32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_split_floats_allow_empty",
+ "return_type": "godot_packed_float32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_split_floats_mk",
+ "return_type": "godot_packed_float32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_splitters"]
+ ]
+ },
+ {
+ "name": "godot_string_split_floats_mk_allow_empty",
+ "return_type": "godot_packed_float32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_splitters"]
+ ]
+ },
+ {
+ "name": "godot_string_split_ints",
+ "return_type": "godot_packed_int32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_split_ints_allow_empty",
+ "return_type": "godot_packed_int32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_split_ints_mk",
+ "return_type": "godot_packed_int32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_splitters"]
+ ]
+ },
+ {
+ "name": "godot_string_split_ints_mk_allow_empty",
+ "return_type": "godot_packed_int32_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_splitters"]
+ ]
+ },
+ {
+ "name": "godot_string_split_spaces",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_lstrip",
"return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_chars"]
]
},
{
- "name": "godot_aabb_get_area",
- "return_type": "godot_real",
+ "name": "godot_string_rstrip",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_chars"]
]
},
{
- "name": "godot_aabb_has_no_area",
+ "name": "godot_string_trim_prefix",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_prefix"]
+ ]
+ },
+ {
+ "name": "godot_string_trim_suffix",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_suffix"]
+ ]
+ },
+ {
+ "name": "godot_string_char_lowercase",
+ "return_type": "godot_char_type",
+ "arguments": [
+ ["godot_char_type", "p_char"]
+ ]
+ },
+ {
+ "name": "godot_string_char_uppercase",
+ "return_type": "godot_char_type",
+ "arguments": [
+ ["godot_char_type", "p_char"]
+ ]
+ },
+ {
+ "name": "godot_string_to_lower",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_to_upper",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_get_basename",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_get_extension",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_left",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_pos"]
+ ]
+ },
+ {
+ "name": "godot_string_ord_at",
+ "return_type": "godot_char_type",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_string_plus_file",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_file"]
+ ]
+ },
+ {
+ "name": "godot_string_right",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_pos"]
+ ]
+ },
+ {
+ "name": "godot_string_repeat",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_count"]
+ ]
+ },
+ {
+ "name": "godot_string_strip_edges",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_bool", "p_left"],
+ ["godot_bool", "p_right"]
+ ]
+ },
+ {
+ "name": "godot_string_strip_escapes",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_erase",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "p_self"],
+ ["godot_int", "p_pos"],
+ ["godot_int", "p_chars"]
+ ]
+ },
+ {
+ "name": "godot_string_ascii",
+ "return_type": "godot_char_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_latin1",
+ "return_type": "godot_char_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_utf8",
+ "return_type": "godot_char_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_parse_utf8",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["godot_string *", "p_self"],
+ ["const char *", "p_utf8"]
]
},
{
- "name": "godot_aabb_has_no_surface",
+ "name": "godot_string_parse_utf8_with_len",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["godot_string *", "p_self"],
+ ["const char *", "p_utf8"],
+ ["godot_int", "p_len"]
]
},
{
- "name": "godot_aabb_intersects",
+ "name": "godot_string_utf16",
+ "return_type": "godot_char16_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_parse_utf16",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
+ ["godot_string *", "p_self"],
+ ["const char16_t *", "p_utf16"]
]
},
{
- "name": "godot_aabb_encloses",
+ "name": "godot_string_parse_utf16_with_len",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
+ ["godot_string *", "p_self"],
+ ["const char16_t *", "p_utf16"],
+ ["godot_int", "p_len"]
]
},
{
- "name": "godot_aabb_merge",
- "return_type": "godot_aabb",
+ "name": "godot_string_hash",
+ "return_type": "uint32_t",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_intersection",
- "return_type": "godot_aabb",
+ "name": "godot_string_hash64",
+ "return_type": "uint64_t",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_intersects_plane",
+ "name": "godot_string_hash_chars",
+ "return_type": "uint32_t",
+ "arguments": [
+ ["const char *", "p_cstr"]
+ ]
+ },
+ {
+ "name": "godot_string_hash_chars_with_len",
+ "return_type": "uint32_t",
+ "arguments": [
+ ["const char *", "p_cstr"],
+ ["godot_int", "p_len"]
+ ]
+ },
+ {
+ "name": "godot_string_hash_wide_chars",
+ "return_type": "uint32_t",
+ "arguments": [
+ ["const wchar_t *", "p_str"]
+ ]
+ },
+ {
+ "name": "godot_string_hash_wide_chars_with_len",
+ "return_type": "uint32_t",
+ "arguments": [
+ ["const wchar_t *", "p_str"],
+ ["godot_int", "p_len"]
+ ]
+ },
+ {
+ "name": "godot_string_md5_buffer",
+ "return_type": "godot_packed_byte_array",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_md5_text",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_sha1_buffer",
+ "return_type": "godot_packed_byte_array",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_sha1_text",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_sha256_buffer",
+ "return_type": "godot_packed_byte_array",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_sha256_text",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_empty",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_plane *", "p_plane"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_intersects_segment",
+ "name": "godot_string_get_base_dir",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_get_file",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_humanize_size",
+ "return_type": "godot_string",
+ "arguments": [
+ ["size_t", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_string_is_abs_path",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_from"],
- ["const godot_vector3 *", "p_to"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_has_point",
+ "name": "godot_string_is_rel_path",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_point"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_get_support",
- "return_type": "godot_vector3",
+ "name": "godot_string_is_resource_file",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_dir"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_get_longest_axis",
- "return_type": "godot_vector3",
+ "name": "godot_string_path_to",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_path"]
]
},
{
- "name": "godot_aabb_get_longest_axis_index",
- "return_type": "godot_int",
+ "name": "godot_string_path_to_file",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_path"]
]
},
{
- "name": "godot_aabb_get_longest_axis_size",
- "return_type": "godot_real",
+ "name": "godot_string_simplify_path",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_get_shortest_axis",
- "return_type": "godot_vector3",
+ "name": "godot_string_c_escape",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_get_shortest_axis_index",
- "return_type": "godot_int",
+ "name": "godot_string_c_escape_multiline",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_get_shortest_axis_size",
- "return_type": "godot_real",
+ "name": "godot_string_c_unescape",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_expand",
- "return_type": "godot_aabb",
+ "name": "godot_string_http_escape",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_to_point"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_grow",
- "return_type": "godot_aabb",
+ "name": "godot_string_http_unescape",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_real", "p_by"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_get_endpoint",
- "return_type": "godot_vector3",
+ "name": "godot_string_json_escape",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_aabb_operator_equal",
+ "name": "godot_string_xml_escape",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_xml_escape_with_quotes",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_xml_unescape",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_percent_decode",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_percent_encode",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_join",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_parts"]
+ ]
+ },
+ {
+ "name": "godot_string_is_valid_filename",
"return_type": "godot_bool",
"arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_b"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_rid_new",
+ "name": "godot_string_is_valid_float",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_is_valid_hex_number",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_bool", "p_with_prefix"]
+ ]
+ },
+ {
+ "name": "godot_string_is_valid_html_color",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_is_valid_identifier",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_is_valid_integer",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_is_valid_ip_address",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_destroy",
"return_type": "void",
"arguments": [
- ["godot_rid *", "r_dest"]
+ ["godot_string *", "p_self"]
]
},
{
- "name": "godot_rid_get_id",
- "return_type": "godot_int",
+ "name": "godot_string_name_new",
+ "return_type": "void",
"arguments": [
- ["const godot_rid *", "p_self"]
+ ["godot_string_name *", "r_dest"],
+ ["const godot_string *", "p_name"]
]
},
{
- "name": "godot_rid_new_with_resource",
+ "name": "godot_string_name_new_data",
"return_type": "void",
"arguments": [
- ["godot_rid *", "r_dest"],
- ["const godot_object *", "p_from"]
+ ["godot_string_name *", "r_dest"],
+ ["const char *", "p_name"]
]
},
{
- "name": "godot_rid_operator_equal",
+ "name": "godot_string_name_get_name",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string_name *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_name_get_hash",
+ "return_type": "uint32_t",
+ "arguments": [
+ ["const godot_string_name *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_name_get_data_unique_pointer",
+ "return_type": "const void *",
+ "arguments": [
+ ["const godot_string_name *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_name_operator_equal",
"return_type": "godot_bool",
"arguments": [
- ["const godot_rid *", "p_self"],
- ["const godot_rid *", "p_b"]
+ ["const godot_string_name *", "p_self"],
+ ["const godot_string_name *", "p_other"]
]
},
{
- "name": "godot_rid_operator_less",
+ "name": "godot_string_name_operator_less",
"return_type": "godot_bool",
"arguments": [
- ["const godot_rid *", "p_self"],
- ["const godot_rid *", "p_b"]
+ ["const godot_string_name *", "p_self"],
+ ["const godot_string_name *", "p_other"]
+ ]
+ },
+ {
+ "name": "godot_string_name_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string_name *", "p_self"]
]
},
{
@@ -3525,6 +5143,14 @@
]
},
{
+ "name": "godot_transform_new_with_quat",
+ "return_type": "void",
+ "arguments": [
+ ["godot_transform *", "r_dest"],
+ ["const godot_quat *", "p_quat"]
+ ]
+ },
+ {
"name": "godot_transform_new",
"return_type": "void",
"arguments": [
@@ -3931,6 +5557,14 @@
]
},
{
+ "name": "godot_variant_new_string_name",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_string_name *", "p_s"]
+ ]
+ },
+ {
"name": "godot_variant_new_vector2",
"return_type": "void",
"arguments": [
@@ -3939,6 +5573,14 @@
]
},
{
+ "name": "godot_variant_new_vector2i",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_vector2i *", "p_v2"]
+ ]
+ },
+ {
"name": "godot_variant_new_rect2",
"return_type": "void",
"arguments": [
@@ -3947,6 +5589,14 @@
]
},
{
+ "name": "godot_variant_new_rect2i",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_rect2i *", "p_rect2"]
+ ]
+ },
+ {
"name": "godot_variant_new_vector3",
"return_type": "void",
"arguments": [
@@ -3955,6 +5605,14 @@
]
},
{
+ "name": "godot_variant_new_vector3i",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_vector3i *", "p_v3"]
+ ]
+ },
+ {
"name": "godot_variant_new_transform2d",
"return_type": "void",
"arguments": [
@@ -4035,6 +5693,22 @@
]
},
{
+ "name": "godot_variant_new_callable",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_callable *", "p_cb"]
+ ]
+ },
+ {
+ "name": "godot_variant_new_signal",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_signal *", "p_signal"]
+ ]
+ },
+ {
"name": "godot_variant_new_dictionary",
"return_type": "void",
"arguments": [
@@ -4059,19 +5733,35 @@
]
},
{
- "name": "godot_variant_new_packed_int_array",
+ "name": "godot_variant_new_packed_int32_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_packed_int32_array *", "p_pia"]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_int64_array",
+ "return_type": "void",
+ "arguments": [
+ ["godot_variant *", "r_dest"],
+ ["const godot_packed_int64_array *", "p_pia"]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_float32_array",
"return_type": "void",
"arguments": [
["godot_variant *", "r_dest"],
- ["const godot_packed_int_array *", "p_pia"]
+ ["const godot_packed_float32_array *", "p_pra"]
]
},
{
- "name": "godot_variant_new_packed_real_array",
+ "name": "godot_variant_new_packed_float64_array",
"return_type": "void",
"arguments": [
["godot_variant *", "r_dest"],
- ["const godot_packed_real_array *", "p_pra"]
+ ["const godot_packed_float64_array *", "p_pra"]
]
},
{
@@ -4142,6 +5832,13 @@
]
},
{
+ "name": "godot_variant_as_string_name",
+ "return_type": "godot_string_name",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
"name": "godot_variant_as_vector2",
"return_type": "godot_vector2",
"arguments": [
@@ -4149,6 +5846,13 @@
]
},
{
+ "name": "godot_variant_as_vector2i",
+ "return_type": "godot_vector2i",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
"name": "godot_variant_as_rect2",
"return_type": "godot_rect2",
"arguments": [
@@ -4156,6 +5860,13 @@
]
},
{
+ "name": "godot_variant_as_rect2i",
+ "return_type": "godot_rect2i",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
"name": "godot_variant_as_vector3",
"return_type": "godot_vector3",
"arguments": [
@@ -4163,6 +5874,13 @@
]
},
{
+ "name": "godot_variant_as_vector3i",
+ "return_type": "godot_vector3i",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
"name": "godot_variant_as_transform2d",
"return_type": "godot_transform2d",
"arguments": [
@@ -4233,6 +5951,20 @@
]
},
{
+ "name": "godot_variant_as_callable",
+ "return_type": "godot_callable",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_variant_as_signal",
+ "return_type": "godot_signal",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
"name": "godot_variant_as_dictionary",
"return_type": "godot_dictionary",
"arguments": [
@@ -4254,15 +5986,29 @@
]
},
{
- "name": "godot_variant_as_packed_int_array",
- "return_type": "godot_packed_int_array",
+ "name": "godot_variant_as_packed_int32_array",
+ "return_type": "godot_packed_int32_array",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_int64_array",
+ "return_type": "godot_packed_int64_array",
+ "arguments": [
+ ["const godot_variant *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_float32_array",
+ "return_type": "godot_packed_float32_array",
"arguments": [
["const godot_variant *", "p_self"]
]
},
{
- "name": "godot_variant_as_packed_real_array",
- "return_type": "godot_packed_real_array",
+ "name": "godot_variant_as_packed_float64_array",
+ "return_type": "godot_packed_float64_array",
"arguments": [
["const godot_variant *", "p_self"]
]
@@ -4315,15 +6061,14 @@
]
},
{
- "name": "godot_variant_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_variant_hash",
+ "return_type": "uint32_t",
"arguments": [
- ["const godot_variant *", "p_self"],
- ["const godot_variant *", "p_other"]
+ ["const godot_variant *", "p_self"]
]
},
{
- "name": "godot_variant_operator_less",
+ "name": "godot_variant_operator_equal",
"return_type": "godot_bool",
"arguments": [
["const godot_variant *", "p_self"],
@@ -4331,7 +6076,7 @@
]
},
{
- "name": "godot_variant_hash_compare",
+ "name": "godot_variant_operator_less",
"return_type": "godot_bool",
"arguments": [
["const godot_variant *", "p_self"],
@@ -4339,1236 +6084,1005 @@
]
},
{
- "name": "godot_variant_booleanize",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_char_string_length",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_char_string *", "p_cs"]
- ]
- },
- {
- "name": "godot_char_string_get_data",
- "return_type": "const char *",
+ "name": "godot_variant_get_operator_name",
+ "return_type": "godot_string",
"arguments": [
- ["const godot_char_string *", "p_cs"]
+ ["godot_variant_operator", "p_op"]
]
},
{
- "name": "godot_char_string_destroy",
+ "name": "godot_variant_evaluate",
"return_type": "void",
"arguments": [
- ["godot_char_string *", "p_cs"]
+ ["godot_variant_operator", "p_op"],
+ ["const godot_variant *", "p_a"],
+ ["const godot_variant *", "p_b"],
+ ["godot_variant *", "r_ret"],
+ ["godot_bool *", "r_valid"]
]
},
{
- "name": "godot_string_new",
- "return_type": "void",
+ "name": "godot_variant_hash_compare",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_string *", "r_dest"]
+ ["const godot_variant *", "p_self"],
+ ["const godot_variant *", "p_other"]
]
},
{
- "name": "godot_string_new_copy",
- "return_type": "void",
+ "name": "godot_variant_booleanize",
+ "return_type": "godot_bool",
"arguments": [
- ["godot_string *", "r_dest"],
- ["const godot_string *", "p_src"]
+ ["const godot_variant *", "p_self"]
]
},
{
- "name": "godot_string_new_with_wide_string",
+ "name": "godot_variant_destroy",
"return_type": "void",
"arguments": [
- ["godot_string *", "r_dest"],
- ["const wchar_t *", "p_contents"],
- ["const int", "p_size"]
+ ["godot_variant *", "p_self"]
]
},
{
- "name": "godot_string_operator_index",
- "return_type": "const wchar_t *",
+ "name": "godot_vector2_as_vector2i",
+ "return_type": "godot_vector2i",
"arguments": [
- ["godot_string *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_operator_index_const",
- "return_type": "wchar_t",
+ "name": "godot_vector2_sign",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_int", "p_idx"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_wide_str",
- "return_type": "const wchar_t *",
+ "name": "godot_vector2_move_toward",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"],
+ ["const godot_real", "p_delta"]
]
},
{
- "name": "godot_string_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_vector2_direction_to",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_b"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"]
]
},
{
- "name": "godot_string_operator_less",
- "return_type": "godot_bool",
+ "name": "godot_vector2_new",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_b"]
+ ["godot_vector2 *", "r_dest"],
+ ["const godot_real", "p_x"],
+ ["const godot_real", "p_y"]
]
},
{
- "name": "godot_string_operator_plus",
+ "name": "godot_vector2_as_string",
"return_type": "godot_string",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_b"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_length",
- "return_type": "godot_int",
+ "name": "godot_vector2_normalized",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_casecmp_to",
- "return_type": "signed char",
+ "name": "godot_vector2_length",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_str"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_nocasecmp_to",
- "return_type": "signed char",
+ "name": "godot_vector2_angle",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_str"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_naturalnocasecmp_to",
- "return_type": "signed char",
+ "name": "godot_vector2_length_squared",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_str"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_begins_with",
+ "name": "godot_vector2_is_normalized",
"return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_begins_with_char_array",
- "return_type": "godot_bool",
+ "name": "godot_vector2_distance_to",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const char *", "p_char_array"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"]
]
},
{
- "name": "godot_string_bigrams",
- "return_type": "godot_array",
+ "name": "godot_vector2_distance_squared_to",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"]
]
},
{
- "name": "godot_string_chr",
- "return_type": "godot_string",
+ "name": "godot_vector2_angle_to",
+ "return_type": "godot_real",
"arguments": [
- ["wchar_t", "p_character"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"]
]
},
{
- "name": "godot_string_ends_with",
- "return_type": "godot_bool",
+ "name": "godot_vector2_angle_to_point",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_to"]
]
},
{
- "name": "godot_string_find",
- "return_type": "godot_int",
+ "name": "godot_vector2_lerp",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"],
+ ["const godot_real", "p_t"]
]
},
{
- "name": "godot_string_find_from",
- "return_type": "godot_int",
+ "name": "godot_vector2_cubic_interpolate",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
- ["godot_int", "p_from"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"],
+ ["const godot_vector2 *", "p_pre_a"],
+ ["const godot_vector2 *", "p_post_b"],
+ ["const godot_real", "p_t"]
]
},
{
- "name": "godot_string_findmk",
- "return_type": "godot_int",
+ "name": "godot_vector2_rotated",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_keys"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_real", "p_phi"]
]
},
{
- "name": "godot_string_findmk_from",
- "return_type": "godot_int",
+ "name": "godot_vector2_tangent",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_keys"],
- ["godot_int", "p_from"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_findmk_from_in_place",
- "return_type": "godot_int",
+ "name": "godot_vector2_floor",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_keys"],
- ["godot_int", "p_from"],
- ["godot_int *", "r_key"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_findn",
- "return_type": "godot_int",
+ "name": "godot_vector2_snapped",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_by"]
]
},
{
- "name": "godot_string_findn_from",
- "return_type": "godot_int",
+ "name": "godot_vector2_aspect",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
- ["godot_int", "p_from"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_find_last",
- "return_type": "godot_int",
+ "name": "godot_vector2_dot",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_with"]
]
},
{
- "name": "godot_string_format",
- "return_type": "godot_string",
+ "name": "godot_vector2_slide",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_variant *", "p_values"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_n"]
]
},
{
- "name": "godot_string_format_with_custom_placeholder",
- "return_type": "godot_string",
+ "name": "godot_vector2_bounce",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_variant *", "p_values"],
- ["const char *", "p_placeholder"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_n"]
]
},
{
- "name": "godot_string_hex_encode_buffer",
- "return_type": "godot_string",
+ "name": "godot_vector2_reflect",
+ "return_type": "godot_vector2",
"arguments": [
- ["const uint8_t *", "p_buffer"],
- ["godot_int", "p_len"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_n"]
]
},
{
- "name": "godot_string_hex_to_int",
- "return_type": "godot_int",
+ "name": "godot_vector2_abs",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_hex_to_int_without_prefix",
- "return_type": "godot_int",
+ "name": "godot_vector2_clamped",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_real", "p_length"]
]
},
{
- "name": "godot_string_insert",
- "return_type": "godot_string",
+ "name": "godot_vector2_operator_add",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_at_pos"],
- ["godot_string", "p_string"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"]
]
},
{
- "name": "godot_string_is_numeric",
- "return_type": "godot_bool",
+ "name": "godot_vector2_operator_subtract",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"]
]
},
{
- "name": "godot_string_is_subsequence_of",
- "return_type": "godot_bool",
+ "name": "godot_vector2_operator_multiply_vector",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"]
]
},
{
- "name": "godot_string_is_subsequence_ofi",
- "return_type": "godot_bool",
+ "name": "godot_vector2_operator_multiply_scalar",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_real", "p_b"]
]
},
{
- "name": "godot_string_lpad",
- "return_type": "godot_string",
+ "name": "godot_vector2_operator_divide_vector",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"]
]
},
{
- "name": "godot_string_lpad_with_custom_character",
- "return_type": "godot_string",
+ "name": "godot_vector2_operator_divide_scalar",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"],
- ["const godot_string *", "p_character"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_real", "p_b"]
]
},
{
- "name": "godot_string_match",
+ "name": "godot_vector2_operator_equal",
"return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_wildcard"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"]
]
},
{
- "name": "godot_string_matchn",
+ "name": "godot_vector2_operator_less",
"return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_wildcard"]
- ]
- },
- {
- "name": "godot_string_md5",
- "return_type": "godot_string",
- "arguments": [
- ["const uint8_t *", "p_md5"]
- ]
- },
- {
- "name": "godot_string_num",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"]
- ]
- },
- {
- "name": "godot_string_num_int64",
- "return_type": "godot_string",
- "arguments": [
- ["int64_t", "p_num"],
- ["godot_int", "p_base"]
- ]
- },
- {
- "name": "godot_string_num_int64_capitalized",
- "return_type": "godot_string",
- "arguments": [
- ["int64_t", "p_num"],
- ["godot_int", "p_base"],
- ["godot_bool", "p_capitalize_hex"]
- ]
- },
- {
- "name": "godot_string_num_real",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"]
- ]
- },
- {
- "name": "godot_string_num_scientific",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"]
- ]
- },
- {
- "name": "godot_string_num_with_decimals",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"],
- ["godot_int", "p_decimals"]
- ]
- },
- {
- "name": "godot_string_pad_decimals",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_digits"]
- ]
- },
- {
- "name": "godot_string_pad_zeros",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_digits"]
- ]
- },
- {
- "name": "godot_string_replace_first",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_key"],
- ["godot_string", "p_with"]
- ]
- },
- {
- "name": "godot_string_replace",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_key"],
- ["godot_string", "p_with"]
- ]
- },
- {
- "name": "godot_string_replacen",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_key"],
- ["godot_string", "p_with"]
- ]
- },
- {
- "name": "godot_string_rfind",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
- ]
- },
- {
- "name": "godot_string_rfindn",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_vector2 *", "p_self"],
+ ["const godot_vector2 *", "p_b"]
]
},
{
- "name": "godot_string_rfind_from",
- "return_type": "godot_int",
+ "name": "godot_vector2_operator_neg",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
- ["godot_int", "p_from"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_rfindn_from",
- "return_type": "godot_int",
+ "name": "godot_vector2_set_x",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
- ["godot_int", "p_from"]
+ ["godot_vector2 *", "p_self"],
+ ["const godot_real", "p_x"]
]
},
{
- "name": "godot_string_rpad",
- "return_type": "godot_string",
+ "name": "godot_vector2_set_y",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"]
+ ["godot_vector2 *", "p_self"],
+ ["const godot_real", "p_y"]
]
},
{
- "name": "godot_string_rpad_with_custom_character",
- "return_type": "godot_string",
+ "name": "godot_vector2_get_x",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"],
- ["const godot_string *", "p_character"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_similarity",
+ "name": "godot_vector2_get_y",
"return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
+ ["const godot_vector2 *", "p_self"]
]
},
{
- "name": "godot_string_sprintf",
- "return_type": "godot_string",
+ "name": "godot_vector2i_new",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_values"],
- ["godot_bool *", "p_error"]
+ ["godot_vector2i *", "r_dest"],
+ ["const godot_int", "p_x"],
+ ["const godot_int", "p_y"]
]
},
{
- "name": "godot_string_substr",
+ "name": "godot_vector2i_as_string",
"return_type": "godot_string",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_from"],
- ["godot_int", "p_chars"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_to_double",
- "return_type": "double",
+ "name": "godot_vector2i_as_vector2",
+ "return_type": "godot_vector2",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_to_float",
+ "name": "godot_vector2i_aspect",
"return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_to_int",
- "return_type": "godot_int",
+ "name": "godot_vector2i_abs",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_camelcase_to_underscore",
- "return_type": "godot_string",
+ "name": "godot_vector2i_sign",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_camelcase_to_underscore_lowercased",
- "return_type": "godot_string",
+ "name": "godot_vector2i_operator_add",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_vector2i *", "p_b"]
]
},
{
- "name": "godot_string_capitalize",
- "return_type": "godot_string",
+ "name": "godot_vector2i_operator_subtract",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_vector2i *", "p_b"]
]
},
{
- "name": "godot_string_char_to_double",
- "return_type": "double",
+ "name": "godot_vector2i_operator_multiply_vector",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const char *", "p_what"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_vector2i *", "p_b"]
]
},
{
- "name": "godot_string_char_to_int",
- "return_type": "godot_int",
+ "name": "godot_vector2i_operator_multiply_scalar",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const char *", "p_what"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_int", "p_b"]
]
},
{
- "name": "godot_string_wchar_to_int",
- "return_type": "int64_t",
+ "name": "godot_vector2i_operator_divide_vector",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const wchar_t *", "p_str"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_vector2i *", "p_b"]
]
},
{
- "name": "godot_string_char_to_int_with_len",
- "return_type": "godot_int",
+ "name": "godot_vector2i_operator_divide_scalar",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const char *", "p_what"],
- ["godot_int", "p_len"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_int", "p_b"]
]
},
{
- "name": "godot_string_char_to_int64_with_len",
- "return_type": "int64_t",
+ "name": "godot_vector2i_operator_equal",
+ "return_type": "godot_bool",
"arguments": [
- ["const wchar_t *", "p_str"],
- ["int", "p_len"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_vector2i *", "p_b"]
]
},
{
- "name": "godot_string_hex_to_int64",
- "return_type": "int64_t",
+ "name": "godot_vector2i_operator_less",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"],
+ ["const godot_vector2i *", "p_b"]
]
},
{
- "name": "godot_string_hex_to_int64_with_prefix",
- "return_type": "int64_t",
+ "name": "godot_vector2i_operator_neg",
+ "return_type": "godot_vector2i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_to_int64",
- "return_type": "int64_t",
+ "name": "godot_vector2i_set_x",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["godot_vector2i *", "p_self"],
+ ["const godot_int", "p_x"]
]
},
{
- "name": "godot_string_unicode_char_to_double",
- "return_type": "double",
+ "name": "godot_vector2i_set_y",
+ "return_type": "void",
"arguments": [
- ["const wchar_t *", "p_str"],
- ["const wchar_t **", "r_end"]
+ ["godot_vector2i *", "p_self"],
+ ["const godot_int", "p_y"]
]
},
{
- "name": "godot_string_get_slice_count",
+ "name": "godot_vector2i_get_x",
"return_type": "godot_int",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_get_slice",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_string", "p_splitter"],
- ["godot_int", "p_slice"]
- ]
- },
- {
- "name": "godot_string_get_slicec",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["wchar_t", "p_splitter"],
- ["godot_int", "p_slice"]
- ]
- },
- {
- "name": "godot_string_split",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_allow_empty",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_floats",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_floats_allows_empty",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_floats_mk",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
- ]
- },
- {
- "name": "godot_string_split_floats_mk_allows_empty",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
- ]
- },
- {
- "name": "godot_string_split_ints",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_ints_allows_empty",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_ints_mk",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_split_ints_mk_allows_empty",
- "return_type": "godot_array",
+ "name": "godot_vector2i_get_y",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
+ ["const godot_vector2i *", "p_self"]
]
},
{
- "name": "godot_string_split_spaces",
- "return_type": "godot_array",
+ "name": "godot_vector3_as_vector3i",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_char_lowercase",
- "return_type": "wchar_t",
+ "name": "godot_vector3_sign",
+ "return_type": "godot_vector3",
"arguments": [
- ["wchar_t", "p_char"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_char_uppercase",
- "return_type": "wchar_t",
+ "name": "godot_vector3_move_toward",
+ "return_type": "godot_vector3",
"arguments": [
- ["wchar_t", "p_char"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_to"],
+ ["const godot_real", "p_delta"]
]
},
{
- "name": "godot_string_to_lower",
- "return_type": "godot_string",
+ "name": "godot_vector3_direction_to",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_to"]
]
},
{
- "name": "godot_string_to_upper",
- "return_type": "godot_string",
+ "name": "godot_vector3_new",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["godot_vector3 *", "r_dest"],
+ ["const godot_real", "p_x"],
+ ["const godot_real", "p_y"],
+ ["const godot_real", "p_z"]
]
},
{
- "name": "godot_string_get_basename",
+ "name": "godot_vector3_as_string",
"return_type": "godot_string",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_get_extension",
- "return_type": "godot_string",
+ "name": "godot_vector3_min_axis",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_left",
- "return_type": "godot_string",
+ "name": "godot_vector3_max_axis",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_pos"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_ord_at",
- "return_type": "wchar_t",
+ "name": "godot_vector3_length",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_idx"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_plus_file",
- "return_type": "godot_string",
+ "name": "godot_vector3_length_squared",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_file"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_right",
- "return_type": "godot_string",
+ "name": "godot_vector3_is_normalized",
+ "return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_pos"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_strip_edges",
- "return_type": "godot_string",
+ "name": "godot_vector3_normalized",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_bool", "p_left"],
- ["godot_bool", "p_right"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_strip_escapes",
- "return_type": "godot_string",
+ "name": "godot_vector3_inverse",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_erase",
- "return_type": "void",
+ "name": "godot_vector3_snapped",
+ "return_type": "godot_vector3",
"arguments": [
- ["godot_string *", "p_self"],
- ["godot_int", "p_pos"],
- ["godot_int", "p_chars"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_by"]
]
},
{
- "name": "godot_string_ascii",
- "return_type": "godot_char_string",
+ "name": "godot_vector3_rotated",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_axis"],
+ ["const godot_real", "p_phi"]
]
},
{
- "name": "godot_string_ascii_extended",
- "return_type": "godot_char_string",
+ "name": "godot_vector3_lerp",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"],
+ ["const godot_real", "p_t"]
]
},
{
- "name": "godot_string_utf8",
- "return_type": "godot_char_string",
+ "name": "godot_vector3_cubic_interpolate",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"],
+ ["const godot_vector3 *", "p_pre_a"],
+ ["const godot_vector3 *", "p_post_b"],
+ ["const godot_real", "p_t"]
]
},
{
- "name": "godot_string_parse_utf8",
- "return_type": "godot_bool",
+ "name": "godot_vector3_dot",
+ "return_type": "godot_real",
"arguments": [
- ["godot_string *", "p_self"],
- ["const char *", "p_utf8"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_parse_utf8_with_len",
- "return_type": "godot_bool",
+ "name": "godot_vector3_cross",
+ "return_type": "godot_vector3",
"arguments": [
- ["godot_string *", "p_self"],
- ["const char *", "p_utf8"],
- ["godot_int", "p_len"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_chars_to_utf8",
- "return_type": "godot_string",
+ "name": "godot_vector3_outer",
+ "return_type": "godot_basis",
"arguments": [
- ["const char *", "p_utf8"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_chars_to_utf8_with_len",
- "return_type": "godot_string",
+ "name": "godot_vector3_to_diagonal_matrix",
+ "return_type": "godot_basis",
"arguments": [
- ["const char *", "p_utf8"],
- ["godot_int", "p_len"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_hash",
- "return_type": "uint32_t",
+ "name": "godot_vector3_abs",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_hash64",
- "return_type": "uint64_t",
+ "name": "godot_vector3_floor",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_hash_chars",
- "return_type": "uint32_t",
+ "name": "godot_vector3_ceil",
+ "return_type": "godot_vector3",
"arguments": [
- ["const char *", "p_cstr"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_hash_chars_with_len",
- "return_type": "uint32_t",
+ "name": "godot_vector3_distance_to",
+ "return_type": "godot_real",
"arguments": [
- ["const char *", "p_cstr"],
- ["godot_int", "p_len"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_hash_utf8_chars",
- "return_type": "uint32_t",
+ "name": "godot_vector3_distance_squared_to",
+ "return_type": "godot_real",
"arguments": [
- ["const wchar_t *", "p_str"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_hash_utf8_chars_with_len",
- "return_type": "uint32_t",
+ "name": "godot_vector3_angle_to",
+ "return_type": "godot_real",
"arguments": [
- ["const wchar_t *", "p_str"],
- ["godot_int", "p_len"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_to"]
]
},
{
- "name": "godot_string_md5_buffer",
- "return_type": "godot_packed_byte_array",
+ "name": "godot_vector3_slide",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_n"]
]
},
{
- "name": "godot_string_md5_text",
- "return_type": "godot_string",
+ "name": "godot_vector3_bounce",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_n"]
]
},
{
- "name": "godot_string_sha256_buffer",
- "return_type": "godot_packed_byte_array",
+ "name": "godot_vector3_reflect",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_n"]
]
},
{
- "name": "godot_string_sha256_text",
- "return_type": "godot_string",
+ "name": "godot_vector3_operator_add",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_empty",
- "return_type": "godot_bool",
+ "name": "godot_vector3_operator_subtract",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_get_base_dir",
- "return_type": "godot_string",
+ "name": "godot_vector3_operator_multiply_vector",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_get_file",
- "return_type": "godot_string",
+ "name": "godot_vector3_operator_multiply_scalar",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_real", "p_b"]
]
},
{
- "name": "godot_string_humanize_size",
- "return_type": "godot_string",
+ "name": "godot_vector3_operator_divide_vector",
+ "return_type": "godot_vector3",
"arguments": [
- ["size_t", "p_size"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_is_abs_path",
- "return_type": "godot_bool",
+ "name": "godot_vector3_operator_divide_scalar",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_real", "p_b"]
]
},
{
- "name": "godot_string_is_rel_path",
+ "name": "godot_vector3_operator_equal",
"return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_is_resource_file",
+ "name": "godot_vector3_operator_less",
"return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_path_to",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_path"]
- ]
- },
- {
- "name": "godot_string_path_to_file",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_path"]
- ]
- },
- {
- "name": "godot_string_simplify_path",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3 *", "p_b"]
]
},
{
- "name": "godot_string_c_escape",
- "return_type": "godot_string",
+ "name": "godot_vector3_operator_neg",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"]
]
},
{
- "name": "godot_string_c_escape_multiline",
- "return_type": "godot_string",
+ "name": "godot_vector3_set_axis",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["godot_vector3 *", "p_self"],
+ ["const godot_vector3_axis", "p_axis"],
+ ["const godot_real", "p_val"]
]
},
{
- "name": "godot_string_c_unescape",
- "return_type": "godot_string",
+ "name": "godot_vector3_get_axis",
+ "return_type": "godot_real",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3 *", "p_self"],
+ ["const godot_vector3_axis", "p_axis"]
]
},
{
- "name": "godot_string_http_escape",
- "return_type": "godot_string",
+ "name": "godot_vector3i_new",
+ "return_type": "void",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["godot_vector3i *", "r_dest"],
+ ["const godot_int", "p_x"],
+ ["const godot_int", "p_y"],
+ ["const godot_int", "p_z"]
]
},
{
- "name": "godot_string_http_unescape",
+ "name": "godot_vector3i_as_string",
"return_type": "godot_string",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"]
]
},
{
- "name": "godot_string_json_escape",
- "return_type": "godot_string",
+ "name": "godot_vector3i_as_vector3",
+ "return_type": "godot_vector3",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"]
]
},
{
- "name": "godot_string_word_wrap",
- "return_type": "godot_string",
+ "name": "godot_vector3i_min_axis",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_chars_per_line"]
+ ["const godot_vector3i *", "p_self"]
]
},
{
- "name": "godot_string_xml_escape",
- "return_type": "godot_string",
+ "name": "godot_vector3i_max_axis",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"]
]
},
{
- "name": "godot_string_xml_escape_with_quotes",
- "return_type": "godot_string",
+ "name": "godot_vector3i_abs",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"]
]
},
{
- "name": "godot_string_xml_unescape",
- "return_type": "godot_string",
+ "name": "godot_vector3i_sign",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"]
]
},
{
- "name": "godot_string_percent_decode",
- "return_type": "godot_string",
+ "name": "godot_vector3i_operator_add",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_vector3i *", "p_b"]
]
},
{
- "name": "godot_string_percent_encode",
- "return_type": "godot_string",
+ "name": "godot_vector3i_operator_subtract",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_vector3i *", "p_b"]
]
},
{
- "name": "godot_string_is_valid_float",
- "return_type": "godot_bool",
+ "name": "godot_vector3i_operator_multiply_vector",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_vector3i *", "p_b"]
]
},
{
- "name": "godot_string_is_valid_hex_number",
- "return_type": "godot_bool",
+ "name": "godot_vector3i_operator_multiply_scalar",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"],
- ["godot_bool", "p_with_prefix"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_int", "p_b"]
]
},
{
- "name": "godot_string_is_valid_html_color",
- "return_type": "godot_bool",
+ "name": "godot_vector3i_operator_divide_vector",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_vector3i *", "p_b"]
]
},
{
- "name": "godot_string_is_valid_identifier",
- "return_type": "godot_bool",
+ "name": "godot_vector3i_operator_divide_scalar",
+ "return_type": "godot_vector3i",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_int", "p_b"]
]
},
{
- "name": "godot_string_is_valid_integer",
+ "name": "godot_vector3i_operator_equal",
"return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_vector3i *", "p_b"]
]
},
{
- "name": "godot_string_is_valid_ip_address",
+ "name": "godot_vector3i_operator_less",
"return_type": "godot_bool",
"arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_vector3i *", "p_b"]
]
},
{
- "name": "godot_string_name_new",
- "return_type": "void",
+ "name": "godot_vector3i_operator_neg",
+ "return_type": "godot_vector3i",
"arguments": [
- ["godot_string_name *", "r_dest"],
- ["const godot_string *", "p_name"]
+ ["const godot_vector3i *", "p_self"]
]
},
{
- "name": "godot_string_name_new_data",
+ "name": "godot_vector3i_set_axis",
"return_type": "void",
"arguments": [
- ["godot_string_name *", "r_dest"],
- ["const char *", "p_name"]
+ ["godot_vector3i *", "p_self"],
+ ["const godot_vector3_axis", "p_axis"],
+ ["const godot_int", "p_val"]
]
},
{
- "name": "godot_string_name_get_name",
- "return_type": "godot_string",
+ "name": "godot_vector3i_get_axis",
+ "return_type": "godot_int",
"arguments": [
- ["const godot_string_name *", "p_self"]
+ ["const godot_vector3i *", "p_self"],
+ ["const godot_vector3_axis", "p_axis"]
]
},
{
- "name": "godot_string_name_get_hash",
- "return_type": "uint32_t",
+ "name": "godot_global_get_singleton",
+ "return_type": "godot_object *",
"arguments": [
- ["const godot_string_name *", "p_self"]
+ ["char *", "p_name"]
]
},
{
- "name": "godot_string_name_get_data_unique_pointer",
- "return_type": "const void *",
+ "name": "godot_get_class_tag",
+ "return_type": "void *",
"arguments": [
- ["const godot_string_name *", "p_self"]
+ ["const godot_string_name *", "p_class"]
]
},
{
- "name": "godot_string_name_operator_equal",
- "return_type": "godot_bool",
+ "name": "godot_object_cast_to",
+ "return_type": "godot_object *",
"arguments": [
- ["const godot_string_name *", "p_self"],
- ["const godot_string_name *", "p_other"]
+ ["const godot_object *", "p_object"],
+ ["void *", "p_class_tag"]
]
},
{
- "name": "godot_string_name_operator_less",
- "return_type": "godot_bool",
+ "name": "godot_object_get_instance_id",
+ "return_type": "uint64_t",
"arguments": [
- ["const godot_string_name *", "p_self"],
- ["const godot_string_name *", "p_other"]
+ ["const godot_object *", "p_object"]
]
},
{
- "name": "godot_string_name_destroy",
- "return_type": "void",
+ "name": "godot_instance_from_id",
+ "return_type": "godot_object *",
"arguments": [
- ["godot_string_name *", "p_self"]
+ ["uint64_t", "p_instance_id"]
]
},
{
@@ -5579,13 +7093,6 @@
]
},
{
- "name": "godot_global_get_singleton",
- "return_type": "godot_object *",
- "arguments": [
- ["char *", "p_name"]
- ]
- },
- {
"name": "godot_method_bind_get_method",
"return_type": "godot_method_bind *",
"arguments": [
@@ -5691,132 +7198,10 @@
"name": "nativescript",
"type": "NATIVESCRIPT",
"version": {
- "major": 1,
+ "major": 4,
"minor": 0
},
- "next": {
- "type": "NATIVESCRIPT",
- "version": {
- "major": 1,
- "minor": 1
- },
- "next": null,
- "api": [
- {
- "name": "godot_nativescript_set_method_argument_information",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_function_name"],
- ["int", "p_num_args"],
- ["const godot_method_arg *", "p_args"]
- ]
- },
- {
- "name": "godot_nativescript_set_class_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_method_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_function_name"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_property_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_path"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_signal_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_signal_name"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_global_type_tag",
- "return_type": "void",
- "arguments": [
- ["int", "p_idx"],
- ["const char *", "p_name"],
- ["const void *", "p_type_tag"]
- ]
- },
- {
- "name": "godot_nativescript_get_global_type_tag",
- "return_type": "const void *",
- "arguments": [
- ["int", "p_idx"],
- ["const char *", "p_name"]
- ]
- },
- {
- "name": "godot_nativescript_set_type_tag",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const void *", "p_type_tag"]
- ]
- },
- {
- "name": "godot_nativescript_get_type_tag",
- "return_type": "const void *",
- "arguments": [
- ["const godot_object *", "p_object"]
- ]
- },
- {
- "name": "godot_nativescript_register_instance_binding_data_functions",
- "return_type": "int",
- "arguments": [
- ["godot_instance_binding_functions", "p_binding_functions"]
- ]
- },
- {
- "name": "godot_nativescript_unregister_instance_binding_data_functions",
- "return_type": "void",
- "arguments": [
- ["int", "p_idx"]
- ]
- },
- {
- "name": "godot_nativescript_get_instance_binding_data",
- "return_type": "void *",
- "arguments": [
- ["int", "p_idx"],
- ["godot_object *", "p_object"]
- ]
- },
- {
- "name": "godot_nativescript_profiling_add_data",
- "return_type": "void",
- "arguments": [
- ["const char *", "p_signature"],
- ["uint64_t", "p_line"]
- ]
- }
- ]
- },
+ "next": null,
"api": [
{
"name": "godot_nativescript_register_class",
@@ -5825,8 +7210,8 @@
["void *", "p_gdnative_handle"],
["const char *", "p_name"],
["const char *", "p_base"],
- ["godot_instance_create_func", "p_create_func"],
- ["godot_instance_destroy_func", "p_destroy_func"]
+ ["godot_nativescript_instance_create_func", "p_create_func"],
+ ["godot_nativescript_instance_destroy_func", "p_destroy_func"]
]
},
{
@@ -5836,8 +7221,8 @@
["void *", "p_gdnative_handle"],
["const char *", "p_name"],
["const char *", "p_base"],
- ["godot_instance_create_func", "p_create_func"],
- ["godot_instance_destroy_func", "p_destroy_func"]
+ ["godot_nativescript_instance_create_func", "p_create_func"],
+ ["godot_nativescript_instance_destroy_func", "p_destroy_func"]
]
},
{
@@ -5847,8 +7232,19 @@
["void *", "p_gdnative_handle"],
["const char *", "p_name"],
["const char *", "p_function_name"],
- ["godot_method_attributes", "p_attr"],
- ["godot_instance_method", "p_method"]
+ ["godot_nativescript_method_attributes", "p_attr"],
+ ["godot_nativescript_instance_method", "p_method"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_method_argument_information",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_function_name"],
+ ["int", "p_num_args"],
+ ["const godot_nativescript_method_argument *", "p_args"]
]
},
{
@@ -5858,9 +7254,9 @@
["void *", "p_gdnative_handle"],
["const char *", "p_name"],
["const char *", "p_path"],
- ["godot_property_attributes *", "p_attr"],
- ["godot_property_set_func", "p_set_func"],
- ["godot_property_get_func", "p_get_func"]
+ ["godot_nativescript_property_attributes *", "p_attr"],
+ ["godot_nativescript_property_set_func", "p_set_func"],
+ ["godot_nativescript_property_get_func", "p_get_func"]
]
},
{
@@ -5869,7 +7265,7 @@
"arguments": [
["void *", "p_gdnative_handle"],
["const char *", "p_name"],
- ["const godot_signal *", "p_signal"]
+ ["const godot_nativescript_signal *", "p_signal"]
]
},
{
@@ -5878,6 +7274,108 @@
"arguments": [
["godot_object *", "p_instance"]
]
+ },
+ {
+ "name": "godot_nativescript_set_class_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_method_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_function_name"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_property_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_path"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_signal_documentation",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const char *", "p_signal_name"],
+ ["godot_string", "p_documentation"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_global_type_tag",
+ "return_type": "void",
+ "arguments": [
+ ["int", "p_idx"],
+ ["const char *", "p_name"],
+ ["const void *", "p_type_tag"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_global_type_tag",
+ "return_type": "const void *",
+ "arguments": [
+ ["int", "p_idx"],
+ ["const char *", "p_name"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_type_tag",
+ "return_type": "void",
+ "arguments": [
+ ["void *", "p_gdnative_handle"],
+ ["const char *", "p_name"],
+ ["const void *", "p_type_tag"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_type_tag",
+ "return_type": "const void *",
+ "arguments": [
+ ["const godot_object *", "p_object"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_register_instance_binding_data_functions",
+ "return_type": "int",
+ "arguments": [
+ ["godot_nativescript_instance_binding_functions", "p_binding_functions"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_unregister_instance_binding_data_functions",
+ "return_type": "void",
+ "arguments": [
+ ["int", "p_idx"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_instance_binding_data",
+ "return_type": "void *",
+ "arguments": [
+ ["int", "p_idx"],
+ ["godot_object *", "p_object"]
+ ]
+ },
+ {
+ "name": "godot_nativescript_profiling_add_data",
+ "return_type": "void",
+ "arguments": [
+ ["const char *", "p_signature"],
+ ["uint64_t", "p_line"]
+ ]
}
]
},
@@ -6071,42 +7569,10 @@
"name": "net",
"type": "NET",
"version": {
- "major": 3,
- "minor": 1
- },
- "next": {
- "type": "NET",
- "version": {
- "major": 3,
- "minor": 2
- },
- "next": null,
- "api": [
- {
- "name": "godot_net_set_webrtc_library",
- "return_type": "godot_error",
- "arguments": [
- ["const godot_net_webrtc_library *", "p_library"]
- ]
- },
- {
- "name": "godot_net_bind_webrtc_peer_connection",
- "return_type": "void",
- "arguments": [
- ["godot_object *", "p_obj"],
- ["const godot_net_webrtc_peer_connection *", "p_interface"]
- ]
- },
- {
- "name": "godot_net_bind_webrtc_data_channel",
- "return_type": "void",
- "arguments": [
- ["godot_object *", "p_obj"],
- ["const godot_net_webrtc_data_channel *", "p_interface"]
- ]
- }
- ]
+ "major": 4,
+ "minor": 0
},
+ "next": null,
"api": [
{
"name": "godot_net_bind_stream_peer",
@@ -6131,6 +7597,29 @@
["godot_object *", "p_obj"],
["const godot_net_multiplayer_peer *", "p_interface"]
]
+ },
+ {
+ "name": "godot_net_set_webrtc_library",
+ "return_type": "godot_error",
+ "arguments": [
+ ["const godot_net_webrtc_library *", "p_library"]
+ ]
+ },
+ {
+ "name": "godot_net_bind_webrtc_peer_connection",
+ "return_type": "void",
+ "arguments": [
+ ["godot_object *", "p_obj"],
+ ["const godot_net_webrtc_peer_connection *", "p_interface"]
+ ]
+ },
+ {
+ "name": "godot_net_bind_webrtc_data_channel",
+ "return_type": "void",
+ "arguments": [
+ ["godot_object *", "p_obj"],
+ ["const godot_net_webrtc_data_channel *", "p_interface"]
+ ]
}
]
}
diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py
index 620935795f..28e4957b2f 100644
--- a/modules/gdnative/gdnative_builders.py
+++ b/modules/gdnative/gdnative_builders.py
@@ -74,7 +74,7 @@ def _build_gdnative_api_struct_header(api):
ret_val += [
"typedef struct godot_gdnative_core_"
- + ("{0}_{1}".format(core["version"]["major"], core["version"]["minor"]))
+ + "{0}_{1}".format(core["version"]["major"], core["version"]["minor"])
+ "_api_struct {",
"\tunsigned int type;",
"\tgodot_gdnative_api_version version;",
@@ -185,7 +185,7 @@ def _build_gdnative_api_struct_source(api):
ret_val += [
"extern const godot_gdnative_core_"
- + ("{0}_{1}_api_struct api_{0}_{1}".format(core["version"]["major"], core["version"]["minor"]))
+ + "{0}_{1}_api_struct api_{0}_{1}".format(core["version"]["major"], core["version"]["minor"])
+ " = {",
"\tGDNATIVE_" + core["type"] + ",",
"\t{" + str(core["version"]["major"]) + ", " + str(core["version"]["minor"]) + "},",
@@ -228,7 +228,16 @@ def _build_gdnative_api_struct_source(api):
"extern const godot_gdnative_core_api_struct api_struct = {",
"\tGDNATIVE_" + api["core"]["type"] + ",",
"\t{" + str(api["core"]["version"]["major"]) + ", " + str(api["core"]["version"]["minor"]) + "},",
- "\t(const godot_gdnative_api_struct *)&api_1_1,",
+ "\t"
+ + (
+ "nullptr, "
+ if not api["core"]["next"]
+ else (
+ "(const godot_gdnative_api_struct *)& api_{0}_{1},".format(
+ api["core"]["next"]["version"]["major"], api["core"]["next"]["version"]["minor"]
+ )
+ )
+ ),
"\t" + str(len(api["extensions"])) + ",",
"\tgdnative_extensions_pointers,",
]
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index 2a9836329e..f0f095ddf5 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -127,17 +127,26 @@ void GDNativeLibraryEditor::_on_item_button(Object *item, int column, int id) {
String section = (id == BUTTON_SELECT_DEPENDENCES || id == BUTTON_CLEAR_DEPENDENCES) ? "dependencies" : "entry";
if (id == BUTTON_SELECT_LIBRARY || id == BUTTON_SELECT_DEPENDENCES) {
+ TreeItem *treeItem = Object::cast_to<TreeItem>(item)->get_parent();
EditorFileDialog::FileMode mode = EditorFileDialog::FILE_MODE_OPEN_FILE;
if (id == BUTTON_SELECT_DEPENDENCES) {
mode = EditorFileDialog::FILE_MODE_OPEN_FILES;
+ } else if (treeItem->get_text(0) == "iOS") {
+ mode = EditorFileDialog::FILE_MODE_OPEN_ANY;
}
file_dialog->set_meta("target", target);
file_dialog->set_meta("section", section);
file_dialog->clear_filters();
- file_dialog->add_filter(Object::cast_to<TreeItem>(item)->get_parent()->get_metadata(0));
+
+ String filter_string = treeItem->get_metadata(0);
+ Vector<String> filters = filter_string.split(",", false, 0);
+ for (int i = 0; i < filters.size(); i++) {
+ file_dialog->add_filter(filters[i]);
+ }
+
file_dialog->set_file_mode(mode);
- file_dialog->popup_centered_ratio();
+ file_dialog->popup_file_dialog();
} else if (id == BUTTON_CLEAR_LIBRARY) {
_set_target_value(section, target, "");
@@ -283,7 +292,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platforms["Haiku"] = platform_haiku;
NativePlatformConfig platform_uwp;
- platform_uwp.name = "Windows Universal";
+ platform_uwp.name = "UWP";
platform_uwp.entries.push_back("arm");
platform_uwp.entries.push_back("32");
platform_uwp.entries.push_back("64");
@@ -309,7 +318,10 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platform_ios.name = "iOS";
platform_ios.entries.push_back("armv7");
platform_ios.entries.push_back("arm64");
- platform_ios.library_extension = "*.dylib";
+ platform_ios.entries.push_back("x86_64");
+ // iOS can use both Static and Dynamic libraries.
+ // Frameworks is actually a folder with files.
+ platform_ios.library_extension = "*.framework; Framework, *.xcframework; Binary Framework, *.a; Static Library, *.dylib; Dynamic Library";
platforms["iOS"] = platform_ios;
}
@@ -360,6 +372,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
//file_dialog->set_resizable(true);
add_child(file_dialog);
file_dialog->connect("file_selected", callable_mp(this, &GDNativeLibraryEditor::_on_library_selected));
+ file_dialog->connect("dir_selected", callable_mp(this, &GDNativeLibraryEditor::_on_library_selected));
file_dialog->connect("files_selected", callable_mp(this, &GDNativeLibraryEditor::_on_dependencies_selected));
new_architecture_dialog = memnew(ConfirmationDialog);
diff --git a/modules/gdnative/gdnative_library_editor_plugin.h b/modules/gdnative/gdnative_library_editor_plugin.h
index 5fdb860ca3..180ab7707c 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.h
+++ b/modules/gdnative/gdnative_library_editor_plugin.h
@@ -100,11 +100,11 @@ class GDNativeLibraryEditorPlugin : public EditorPlugin {
Button *button;
public:
- virtual String get_name() const { return "GDNativeLibrary"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
+ virtual String get_name() const override { return "GDNativeLibrary"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_node) override;
+ virtual bool handles(Object *p_node) const override;
+ virtual void make_visible(bool p_visible) override;
GDNativeLibraryEditorPlugin(EditorNode *p_node);
};
diff --git a/modules/gdnative/include/gdnative/aabb.h b/modules/gdnative/include/gdnative/aabb.h
index 9f41e9d4c6..c776297944 100644
--- a/modules/gdnative/include/gdnative/aabb.h
+++ b/modules/gdnative/include/gdnative/aabb.h
@@ -69,6 +69,8 @@ void GDAPI godot_aabb_set_size(const godot_aabb *p_self, const godot_vector3 *p_
godot_string GDAPI godot_aabb_as_string(const godot_aabb *p_self);
+godot_aabb GDAPI godot_aabb_abs(const godot_aabb *p_self);
+
godot_real GDAPI godot_aabb_get_area(const godot_aabb *p_self);
godot_bool GDAPI godot_aabb_has_no_area(const godot_aabb *p_self);
diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h
index e3114e9348..4db685873f 100644
--- a/modules/gdnative/include/gdnative/array.h
+++ b/modules/gdnative/include/gdnative/array.h
@@ -51,7 +51,7 @@ typedef struct {
}
#endif
-#include <gdnative/pool_arrays.h>
+#include <gdnative/packed_arrays.h>
#include <gdnative/variant.h>
#include <gdnative/gdnative.h>
@@ -66,8 +66,10 @@ void GDAPI godot_array_new_packed_color_array(godot_array *r_dest, const godot_p
void GDAPI godot_array_new_packed_vector3_array(godot_array *r_dest, const godot_packed_vector3_array *p_pv3a);
void GDAPI godot_array_new_packed_vector2_array(godot_array *r_dest, const godot_packed_vector2_array *p_pv2a);
void GDAPI godot_array_new_packed_string_array(godot_array *r_dest, const godot_packed_string_array *p_psa);
-void GDAPI godot_array_new_packed_real_array(godot_array *r_dest, const godot_packed_real_array *p_pra);
-void GDAPI godot_array_new_packed_int_array(godot_array *r_dest, const godot_packed_int_array *p_pia);
+void GDAPI godot_array_new_packed_float32_array(godot_array *r_dest, const godot_packed_float32_array *p_pra);
+void GDAPI godot_array_new_packed_float64_array(godot_array *r_dest, const godot_packed_float64_array *p_pra);
+void GDAPI godot_array_new_packed_int32_array(godot_array *r_dest, const godot_packed_int32_array *p_pia);
+void GDAPI godot_array_new_packed_int64_array(godot_array *r_dest, const godot_packed_int64_array *p_pia);
void GDAPI godot_array_new_packed_byte_array(godot_array *r_dest, const godot_packed_byte_array *p_pba);
void GDAPI godot_array_set(godot_array *p_self, const godot_int p_idx, const godot_variant *p_value);
diff --git a/modules/gdnative/include/gdnative/callable.h b/modules/gdnative/include/gdnative/callable.h
new file mode 100644
index 0000000000..dbb5d02590
--- /dev/null
+++ b/modules/gdnative/include/gdnative/callable.h
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* callable.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 GODOT_CALLABLE_H
+#define GODOT_CALLABLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define GODOT_CALLABLE_SIZE (16)
+
+#ifndef GODOT_CORE_API_GODOT_CALLABLE_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_CALLABLE_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_CALLABLE_SIZE];
+} godot_callable;
+#endif
+
+#define GODOT_SIGNAL_SIZE (16)
+
+#ifndef GODOT_CORE_API_GODOT_SIGNAL_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_SIGNAL_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_SIGNAL_SIZE];
+} godot_signal;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <gdnative/gdnative.h>
+#include <gdnative/string_name.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Callable
+
+void GDAPI godot_callable_new_with_object(godot_callable *r_dest, const godot_object *p_object, const godot_string_name *p_method);
+void GDAPI godot_callable_new_with_object_id(godot_callable *r_dest, uint64_t p_objectid, const godot_string_name *p_method);
+void GDAPI godot_callable_new_copy(godot_callable *r_dest, const godot_callable *p_src);
+
+void GDAPI godot_callable_destroy(godot_callable *p_self);
+
+godot_int GDAPI godot_callable_call(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount, godot_variant *r_return_value);
+void GDAPI godot_callable_call_deferred(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount);
+
+godot_bool GDAPI godot_callable_is_null(const godot_callable *p_self);
+godot_bool GDAPI godot_callable_is_custom(const godot_callable *p_self);
+godot_bool GDAPI godot_callable_is_standard(const godot_callable *p_self);
+
+godot_object GDAPI *godot_callable_get_object(const godot_callable *p_self);
+uint64_t GDAPI godot_callable_get_object_id(const godot_callable *p_self);
+godot_string_name GDAPI godot_callable_get_method(const godot_callable *p_self);
+
+uint32_t GDAPI godot_callable_hash(const godot_callable *p_self);
+
+godot_string GDAPI godot_callable_as_string(const godot_callable *p_self);
+
+godot_bool GDAPI godot_callable_operator_equal(const godot_callable *p_self, const godot_callable *p_other);
+godot_bool GDAPI godot_callable_operator_less(const godot_callable *p_self, const godot_callable *p_other);
+
+// Signal
+
+void GDAPI godot_signal_new_with_object(godot_signal *r_dest, const godot_object *p_object, const godot_string_name *p_name);
+void GDAPI godot_signal_new_with_object_id(godot_signal *r_dest, uint64_t p_objectid, const godot_string_name *p_name);
+void GDAPI godot_signal_new_copy(godot_signal *r_dest, const godot_signal *p_src);
+
+void GDAPI godot_signal_destroy(godot_signal *p_self);
+
+godot_int GDAPI godot_signal_emit(const godot_signal *p_self, const godot_variant **p_arguments, godot_int p_argcount);
+
+godot_int GDAPI godot_signal_connect(godot_signal *p_self, const godot_callable *p_callable, const godot_array *p_binds, uint32_t p_flags);
+void GDAPI godot_signal_disconnect(godot_signal *p_self, const godot_callable *p_callable);
+
+godot_bool GDAPI godot_signal_is_null(const godot_signal *p_self);
+godot_bool GDAPI godot_signal_is_connected(const godot_signal *p_self, const godot_callable *p_callable);
+
+godot_array GDAPI godot_signal_get_connections(const godot_signal *p_self);
+
+godot_object GDAPI *godot_signal_get_object(const godot_signal *p_self);
+uint64_t GDAPI godot_signal_get_object_id(const godot_signal *p_self);
+godot_string_name GDAPI godot_signal_get_name(const godot_signal *p_self);
+
+godot_string GDAPI godot_signal_as_string(const godot_signal *p_self);
+
+godot_bool GDAPI godot_signal_operator_equal(const godot_signal *p_self, const godot_signal *p_other);
+godot_bool GDAPI godot_signal_operator_less(const godot_signal *p_self, const godot_signal *p_other);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h
index 6fdca30122..6a0a375da8 100644
--- a/modules/gdnative/include/gdnative/gdnative.h
+++ b/modules/gdnative/include/gdnative/gdnative.h
@@ -144,15 +144,15 @@ typedef void godot_object;
#include <gdnative/string_name.h>
-////// Vector2
+////// Vector2 & Vector2i
#include <gdnative/vector2.h>
-////// Rect2
+////// Rect2 & Rect2i
#include <gdnative/rect2.h>
-////// Vector3
+////// Vector3 & Vector3i
#include <gdnative/vector3.h>
@@ -192,6 +192,10 @@ typedef void godot_object;
#include <gdnative/rid.h>
+/////// Callable & Signal
+
+#include <gdnative/callable.h>
+
/////// Dictionary
#include <gdnative/dictionary.h>
@@ -200,8 +204,8 @@ typedef void godot_object;
#include <gdnative/array.h>
-// single API file for Pool*Array
-#include <gdnative/pool_arrays.h>
+// single API file for Packed*Array
+#include <gdnative/packed_arrays.h>
void GDAPI godot_object_destroy(godot_object *p_o);
@@ -289,7 +293,9 @@ void GDAPI *godot_get_class_tag(const godot_string_name *p_class);
godot_object GDAPI *godot_object_cast_to(const godot_object *p_object, void *p_class_tag);
// equivalent of GDScript's instance_from_id
-godot_object GDAPI *godot_instance_from_id(godot_int p_instance_id);
+godot_object GDAPI *godot_instance_from_id(uint64_t p_instance_id);
+
+uint64_t GDAPI godot_object_get_instance_id(const godot_object *p_object);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/pool_arrays.h b/modules/gdnative/include/gdnative/packed_arrays.h
index c610377f54..6a1727d76f 100644
--- a/modules/gdnative/include/gdnative/pool_arrays.h
+++ b/modules/gdnative/include/gdnative/packed_arrays.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* pool_arrays.h */
+/* packed_arrays.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GODOT_POOL_ARRAYS_H
-#define GODOT_POOL_ARRAYS_H
+#ifndef GODOT_PACKED_ARRAYS_H
+#define GODOT_PACKED_ARRAYS_H
#ifdef __cplusplus
extern "C" {
@@ -39,7 +39,7 @@ extern "C" {
/////// PackedByteArray
-#define GODOT_PACKED_BYTE_ARRAY_SIZE sizeof(void *)
+#define GODOT_PACKED_BYTE_ARRAY_SIZE (2 * sizeof(void *))
#ifndef GODOT_CORE_API_GODOT_PACKED_BYTE_ARRAY_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_PACKED_BYTE_ARRAY_TYPE_DEFINED
@@ -50,29 +50,51 @@ typedef struct {
/////// PackedInt32Array
-#define GODOT_PACKED_INT_ARRAY_SIZE sizeof(void *)
+#define GODOT_PACKED_INT32_ARRAY_SIZE (2 * sizeof(void *))
-#ifndef GODOT_CORE_API_GODOT_PACKED_INT_ARRAY_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_PACKED_INT_ARRAY_TYPE_DEFINED
+#ifndef GODOT_CORE_API_GODOT_PACKED_INT32_ARRAY_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_PACKED_INT32_ARRAY_TYPE_DEFINED
typedef struct {
- uint8_t _dont_touch_that[GODOT_PACKED_INT_ARRAY_SIZE];
-} godot_packed_int_array;
+ uint8_t _dont_touch_that[GODOT_PACKED_INT32_ARRAY_SIZE];
+} godot_packed_int32_array;
+#endif
+
+/////// PackedInt64Array
+
+#define GODOT_PACKED_INT64_ARRAY_SIZE (2 * sizeof(void *))
+
+#ifndef GODOT_CORE_API_GODOT_PACKED_INT64_ARRAY_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_PACKED_INT64_ARRAY_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_INT64_ARRAY_SIZE];
+} godot_packed_int64_array;
#endif
/////// PackedFloat32Array
-#define GODOT_PACKED_REAL_ARRAY_SIZE sizeof(void *)
+#define GODOT_PACKED_FLOAT32_ARRAY_SIZE (2 * sizeof(void *))
+
+#ifndef GODOT_CORE_API_GODOT_PACKED_FLOAT32_ARRAY_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_PACKED_FLOAT32_ARRAY_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_FLOAT32_ARRAY_SIZE];
+} godot_packed_float32_array;
+#endif
+
+/////// PackedFloat64Array
+
+#define GODOT_PACKED_FLOAT64_ARRAY_SIZE (2 * sizeof(void *))
-#ifndef GODOT_CORE_API_GODOT_PACKED_REAL_ARRAY_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_PACKED_REAL_ARRAY_TYPE_DEFINED
+#ifndef GODOT_CORE_API_GODOT_PACKED_FLOAT64_ARRAY_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_PACKED_FLOAT64_ARRAY_TYPE_DEFINED
typedef struct {
- uint8_t _dont_touch_that[GODOT_PACKED_REAL_ARRAY_SIZE];
-} godot_packed_real_array;
+ uint8_t _dont_touch_that[GODOT_PACKED_FLOAT64_ARRAY_SIZE];
+} godot_packed_float64_array;
#endif
/////// PackedStringArray
-#define GODOT_PACKED_STRING_ARRAY_SIZE sizeof(void *)
+#define GODOT_PACKED_STRING_ARRAY_SIZE (2 * sizeof(void *))
#ifndef GODOT_CORE_API_GODOT_PACKED_STRING_ARRAY_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_PACKED_STRING_ARRAY_TYPE_DEFINED
@@ -83,7 +105,7 @@ typedef struct {
/////// PackedVector2Array
-#define GODOT_PACKED_VECTOR2_ARRAY_SIZE sizeof(void *)
+#define GODOT_PACKED_VECTOR2_ARRAY_SIZE (2 * sizeof(void *))
#ifndef GODOT_CORE_API_GODOT_PACKED_VECTOR2_ARRAY_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_PACKED_VECTOR2_ARRAY_TYPE_DEFINED
@@ -94,7 +116,7 @@ typedef struct {
/////// PackedVector3Array
-#define GODOT_PACKED_VECTOR3_ARRAY_SIZE sizeof(void *)
+#define GODOT_PACKED_VECTOR3_ARRAY_SIZE (2 * sizeof(void *))
#ifndef GODOT_CORE_API_GODOT_PACKED_VECTOR3_ARRAY_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_PACKED_VECTOR3_ARRAY_TYPE_DEFINED
@@ -105,7 +127,7 @@ typedef struct {
/////// PackedColorArray
-#define GODOT_PACKED_COLOR_ARRAY_SIZE sizeof(void *)
+#define GODOT_PACKED_COLOR_ARRAY_SIZE (2 * sizeof(void *))
#ifndef GODOT_CORE_API_GODOT_PACKED_COLOR_ARRAY_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_PACKED_COLOR_ARRAY_TYPE_DEFINED
@@ -136,12 +158,19 @@ void GDAPI godot_packed_byte_array_new(godot_packed_byte_array *r_dest);
void GDAPI godot_packed_byte_array_new_copy(godot_packed_byte_array *r_dest, const godot_packed_byte_array *p_src);
void GDAPI godot_packed_byte_array_new_with_array(godot_packed_byte_array *r_dest, const godot_array *p_a);
+const uint8_t GDAPI *godot_packed_byte_array_ptr(const godot_packed_byte_array *p_self);
+uint8_t GDAPI *godot_packed_byte_array_ptrw(godot_packed_byte_array *p_self);
+
void GDAPI godot_packed_byte_array_append(godot_packed_byte_array *p_self, const uint8_t p_data);
void GDAPI godot_packed_byte_array_append_array(godot_packed_byte_array *p_self, const godot_packed_byte_array *p_array);
godot_error GDAPI godot_packed_byte_array_insert(godot_packed_byte_array *p_self, const godot_int p_idx, const uint8_t p_data);
+godot_bool GDAPI godot_packed_byte_array_has(godot_packed_byte_array *p_self, const uint8_t p_value);
+
+void GDAPI godot_packed_byte_array_sort(godot_packed_byte_array *p_self);
+
void GDAPI godot_packed_byte_array_invert(godot_packed_byte_array *p_self);
void GDAPI godot_packed_byte_array_push_back(godot_packed_byte_array *p_self, const uint8_t p_data);
@@ -159,63 +188,149 @@ godot_bool GDAPI godot_packed_byte_array_empty(const godot_packed_byte_array *p_
void GDAPI godot_packed_byte_array_destroy(godot_packed_byte_array *p_self);
-// int
+// int32
+
+void GDAPI godot_packed_int32_array_new(godot_packed_int32_array *r_dest);
+void GDAPI godot_packed_int32_array_new_copy(godot_packed_int32_array *r_dest, const godot_packed_int32_array *p_src);
+void GDAPI godot_packed_int32_array_new_with_array(godot_packed_int32_array *r_dest, const godot_array *p_a);
+
+const int32_t GDAPI *godot_packed_int32_array_ptr(const godot_packed_int32_array *p_self);
+int32_t GDAPI *godot_packed_int32_array_ptrw(godot_packed_int32_array *p_self);
+
+void GDAPI godot_packed_int32_array_append(godot_packed_int32_array *p_self, const int32_t p_data);
+
+void GDAPI godot_packed_int32_array_append_array(godot_packed_int32_array *p_self, const godot_packed_int32_array *p_array);
+
+godot_error GDAPI godot_packed_int32_array_insert(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data);
+
+godot_bool GDAPI godot_packed_int32_array_has(godot_packed_int32_array *p_self, const int32_t p_value);
+
+void GDAPI godot_packed_int32_array_sort(godot_packed_int32_array *p_self);
+
+void GDAPI godot_packed_int32_array_invert(godot_packed_int32_array *p_self);
+
+void GDAPI godot_packed_int32_array_push_back(godot_packed_int32_array *p_self, const int32_t p_data);
+
+void GDAPI godot_packed_int32_array_remove(godot_packed_int32_array *p_self, const godot_int p_idx);
+
+void GDAPI godot_packed_int32_array_resize(godot_packed_int32_array *p_self, const godot_int p_size);
+
+void GDAPI godot_packed_int32_array_set(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data);
+int32_t GDAPI godot_packed_int32_array_get(const godot_packed_int32_array *p_self, const godot_int p_idx);
+
+godot_int GDAPI godot_packed_int32_array_size(const godot_packed_int32_array *p_self);
+
+godot_bool GDAPI godot_packed_int32_array_empty(const godot_packed_int32_array *p_self);
+
+void GDAPI godot_packed_int32_array_destroy(godot_packed_int32_array *p_self);
+
+// int64
+
+void GDAPI godot_packed_int64_array_new(godot_packed_int64_array *r_dest);
+void GDAPI godot_packed_int64_array_new_copy(godot_packed_int64_array *r_dest, const godot_packed_int64_array *p_src);
+void GDAPI godot_packed_int64_array_new_with_array(godot_packed_int64_array *r_dest, const godot_array *p_a);
+
+const int64_t GDAPI *godot_packed_int64_array_ptr(const godot_packed_int64_array *p_self);
+int64_t GDAPI *godot_packed_int64_array_ptrw(godot_packed_int64_array *p_self);
+
+void GDAPI godot_packed_int64_array_append(godot_packed_int64_array *p_self, const int64_t p_data);
-void GDAPI godot_packed_int_array_new(godot_packed_int_array *r_dest);
-void GDAPI godot_packed_int_array_new_copy(godot_packed_int_array *r_dest, const godot_packed_int_array *p_src);
-void GDAPI godot_packed_int_array_new_with_array(godot_packed_int_array *r_dest, const godot_array *p_a);
+void GDAPI godot_packed_int64_array_append_array(godot_packed_int64_array *p_self, const godot_packed_int64_array *p_array);
-void GDAPI godot_packed_int_array_append(godot_packed_int_array *p_self, const godot_int p_data);
+godot_error GDAPI godot_packed_int64_array_insert(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data);
-void GDAPI godot_packed_int_array_append_array(godot_packed_int_array *p_self, const godot_packed_int_array *p_array);
+godot_bool GDAPI godot_packed_int64_array_has(godot_packed_int64_array *p_self, const int64_t p_value);
-godot_error GDAPI godot_packed_int_array_insert(godot_packed_int_array *p_self, const godot_int p_idx, const godot_int p_data);
+void GDAPI godot_packed_int64_array_sort(godot_packed_int64_array *p_self);
-void GDAPI godot_packed_int_array_invert(godot_packed_int_array *p_self);
+void GDAPI godot_packed_int64_array_invert(godot_packed_int64_array *p_self);
-void GDAPI godot_packed_int_array_push_back(godot_packed_int_array *p_self, const godot_int p_data);
+void GDAPI godot_packed_int64_array_push_back(godot_packed_int64_array *p_self, const int64_t p_data);
-void GDAPI godot_packed_int_array_remove(godot_packed_int_array *p_self, const godot_int p_idx);
+void GDAPI godot_packed_int64_array_remove(godot_packed_int64_array *p_self, const godot_int p_idx);
-void GDAPI godot_packed_int_array_resize(godot_packed_int_array *p_self, const godot_int p_size);
+void GDAPI godot_packed_int64_array_resize(godot_packed_int64_array *p_self, const godot_int p_size);
-void GDAPI godot_packed_int_array_set(godot_packed_int_array *p_self, const godot_int p_idx, const godot_int p_data);
-godot_int GDAPI godot_packed_int_array_get(const godot_packed_int_array *p_self, const godot_int p_idx);
+void GDAPI godot_packed_int64_array_set(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data);
+int64_t GDAPI godot_packed_int64_array_get(const godot_packed_int64_array *p_self, const godot_int p_idx);
-godot_int GDAPI godot_packed_int_array_size(const godot_packed_int_array *p_self);
+godot_int GDAPI godot_packed_int64_array_size(const godot_packed_int64_array *p_self);
-godot_bool GDAPI godot_packed_int_array_empty(const godot_packed_int_array *p_self);
+godot_bool GDAPI godot_packed_int64_array_empty(const godot_packed_int64_array *p_self);
-void GDAPI godot_packed_int_array_destroy(godot_packed_int_array *p_self);
+void GDAPI godot_packed_int64_array_destroy(godot_packed_int64_array *p_self);
-// real
+// float32
-void GDAPI godot_packed_real_array_new(godot_packed_real_array *r_dest);
-void GDAPI godot_packed_real_array_new_copy(godot_packed_real_array *r_dest, const godot_packed_real_array *p_src);
-void GDAPI godot_packed_real_array_new_with_array(godot_packed_real_array *r_dest, const godot_array *p_a);
+void GDAPI godot_packed_float32_array_new(godot_packed_float32_array *r_dest);
+void GDAPI godot_packed_float32_array_new_copy(godot_packed_float32_array *r_dest, const godot_packed_float32_array *p_src);
+void GDAPI godot_packed_float32_array_new_with_array(godot_packed_float32_array *r_dest, const godot_array *p_a);
-void GDAPI godot_packed_real_array_append(godot_packed_real_array *p_self, const godot_real p_data);
+const float GDAPI *godot_packed_float32_array_ptr(const godot_packed_float32_array *p_self);
+float GDAPI *godot_packed_float32_array_ptrw(godot_packed_float32_array *p_self);
-void GDAPI godot_packed_real_array_append_array(godot_packed_real_array *p_self, const godot_packed_real_array *p_array);
+void GDAPI godot_packed_float32_array_append(godot_packed_float32_array *p_self, const float p_data);
-godot_error GDAPI godot_packed_real_array_insert(godot_packed_real_array *p_self, const godot_int p_idx, const godot_real p_data);
+void GDAPI godot_packed_float32_array_append_array(godot_packed_float32_array *p_self, const godot_packed_float32_array *p_array);
-void GDAPI godot_packed_real_array_invert(godot_packed_real_array *p_self);
+godot_error GDAPI godot_packed_float32_array_insert(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data);
-void GDAPI godot_packed_real_array_push_back(godot_packed_real_array *p_self, const godot_real p_data);
+godot_bool GDAPI godot_packed_float32_array_has(godot_packed_float32_array *p_self, const float p_value);
-void GDAPI godot_packed_real_array_remove(godot_packed_real_array *p_self, const godot_int p_idx);
+void GDAPI godot_packed_float32_array_sort(godot_packed_float32_array *p_self);
-void GDAPI godot_packed_real_array_resize(godot_packed_real_array *p_self, const godot_int p_size);
+void GDAPI godot_packed_float32_array_invert(godot_packed_float32_array *p_self);
-void GDAPI godot_packed_real_array_set(godot_packed_real_array *p_self, const godot_int p_idx, const godot_real p_data);
-godot_real GDAPI godot_packed_real_array_get(const godot_packed_real_array *p_self, const godot_int p_idx);
+void GDAPI godot_packed_float32_array_push_back(godot_packed_float32_array *p_self, const float p_data);
-godot_int GDAPI godot_packed_real_array_size(const godot_packed_real_array *p_self);
+void GDAPI godot_packed_float32_array_remove(godot_packed_float32_array *p_self, const godot_int p_idx);
-godot_bool GDAPI godot_packed_real_array_empty(const godot_packed_real_array *p_self);
+void GDAPI godot_packed_float32_array_resize(godot_packed_float32_array *p_self, const godot_int p_size);
-void GDAPI godot_packed_real_array_destroy(godot_packed_real_array *p_self);
+void GDAPI godot_packed_float32_array_set(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data);
+float GDAPI godot_packed_float32_array_get(const godot_packed_float32_array *p_self, const godot_int p_idx);
+
+godot_int GDAPI godot_packed_float32_array_size(const godot_packed_float32_array *p_self);
+
+godot_bool GDAPI godot_packed_float32_array_empty(const godot_packed_float32_array *p_self);
+
+void GDAPI godot_packed_float32_array_destroy(godot_packed_float32_array *p_self);
+
+// float64
+
+void GDAPI godot_packed_float64_array_new(godot_packed_float64_array *r_dest);
+void GDAPI godot_packed_float64_array_new_copy(godot_packed_float64_array *r_dest, const godot_packed_float64_array *p_src);
+void GDAPI godot_packed_float64_array_new_with_array(godot_packed_float64_array *r_dest, const godot_array *p_a);
+
+const double GDAPI *godot_packed_float64_array_ptr(const godot_packed_float64_array *p_self);
+double GDAPI *godot_packed_float64_array_ptrw(godot_packed_float64_array *p_self);
+
+void GDAPI godot_packed_float64_array_append(godot_packed_float64_array *p_self, const double p_data);
+
+void GDAPI godot_packed_float64_array_append_array(godot_packed_float64_array *p_self, const godot_packed_float64_array *p_array);
+
+godot_error GDAPI godot_packed_float64_array_insert(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data);
+
+godot_bool GDAPI godot_packed_float64_array_has(godot_packed_float64_array *p_self, const double p_value);
+
+void GDAPI godot_packed_float64_array_sort(godot_packed_float64_array *p_self);
+
+void GDAPI godot_packed_float64_array_invert(godot_packed_float64_array *p_self);
+
+void GDAPI godot_packed_float64_array_push_back(godot_packed_float64_array *p_self, const double p_data);
+
+void GDAPI godot_packed_float64_array_remove(godot_packed_float64_array *p_self, const godot_int p_idx);
+
+void GDAPI godot_packed_float64_array_resize(godot_packed_float64_array *p_self, const godot_int p_size);
+
+void GDAPI godot_packed_float64_array_set(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data);
+double GDAPI godot_packed_float64_array_get(const godot_packed_float64_array *p_self, const godot_int p_idx);
+
+godot_int GDAPI godot_packed_float64_array_size(const godot_packed_float64_array *p_self);
+
+godot_bool GDAPI godot_packed_float64_array_empty(const godot_packed_float64_array *p_self);
+
+void GDAPI godot_packed_float64_array_destroy(godot_packed_float64_array *p_self);
// string
@@ -223,12 +338,19 @@ void GDAPI godot_packed_string_array_new(godot_packed_string_array *r_dest);
void GDAPI godot_packed_string_array_new_copy(godot_packed_string_array *r_dest, const godot_packed_string_array *p_src);
void GDAPI godot_packed_string_array_new_with_array(godot_packed_string_array *r_dest, const godot_array *p_a);
+const godot_string GDAPI *godot_packed_string_array_ptr(const godot_packed_string_array *p_self);
+godot_string GDAPI *godot_packed_string_array_ptrw(godot_packed_string_array *p_self);
+
void GDAPI godot_packed_string_array_append(godot_packed_string_array *p_self, const godot_string *p_data);
void GDAPI godot_packed_string_array_append_array(godot_packed_string_array *p_self, const godot_packed_string_array *p_array);
godot_error GDAPI godot_packed_string_array_insert(godot_packed_string_array *p_self, const godot_int p_idx, const godot_string *p_data);
+godot_bool GDAPI godot_packed_string_array_has(godot_packed_string_array *p_self, const godot_string *p_value);
+
+void GDAPI godot_packed_string_array_sort(godot_packed_string_array *p_self);
+
void GDAPI godot_packed_string_array_invert(godot_packed_string_array *p_self);
void GDAPI godot_packed_string_array_push_back(godot_packed_string_array *p_self, const godot_string *p_data);
@@ -252,12 +374,19 @@ void GDAPI godot_packed_vector2_array_new(godot_packed_vector2_array *r_dest);
void GDAPI godot_packed_vector2_array_new_copy(godot_packed_vector2_array *r_dest, const godot_packed_vector2_array *p_src);
void GDAPI godot_packed_vector2_array_new_with_array(godot_packed_vector2_array *r_dest, const godot_array *p_a);
+const godot_vector2 GDAPI *godot_packed_vector2_array_ptr(const godot_packed_vector2_array *p_self);
+godot_vector2 GDAPI *godot_packed_vector2_array_ptrw(godot_packed_vector2_array *p_self);
+
void GDAPI godot_packed_vector2_array_append(godot_packed_vector2_array *p_self, const godot_vector2 *p_data);
void GDAPI godot_packed_vector2_array_append_array(godot_packed_vector2_array *p_self, const godot_packed_vector2_array *p_array);
godot_error GDAPI godot_packed_vector2_array_insert(godot_packed_vector2_array *p_self, const godot_int p_idx, const godot_vector2 *p_data);
+godot_bool GDAPI godot_packed_vector2_array_has(godot_packed_vector2_array *p_self, const godot_vector2 *p_value);
+
+void GDAPI godot_packed_vector2_array_sort(godot_packed_vector2_array *p_self);
+
void GDAPI godot_packed_vector2_array_invert(godot_packed_vector2_array *p_self);
void GDAPI godot_packed_vector2_array_push_back(godot_packed_vector2_array *p_self, const godot_vector2 *p_data);
@@ -281,12 +410,19 @@ void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *r_dest);
void GDAPI godot_packed_vector3_array_new_copy(godot_packed_vector3_array *r_dest, const godot_packed_vector3_array *p_src);
void GDAPI godot_packed_vector3_array_new_with_array(godot_packed_vector3_array *r_dest, const godot_array *p_a);
+const godot_vector3 GDAPI *godot_packed_vector3_array_ptr(const godot_packed_vector3_array *p_self);
+godot_vector3 GDAPI *godot_packed_vector3_array_ptrw(godot_packed_vector3_array *p_self);
+
void GDAPI godot_packed_vector3_array_append(godot_packed_vector3_array *p_self, const godot_vector3 *p_data);
void GDAPI godot_packed_vector3_array_append_array(godot_packed_vector3_array *p_self, const godot_packed_vector3_array *p_array);
godot_error GDAPI godot_packed_vector3_array_insert(godot_packed_vector3_array *p_self, const godot_int p_idx, const godot_vector3 *p_data);
+godot_bool GDAPI godot_packed_vector3_array_has(godot_packed_vector3_array *p_self, const godot_vector3 *p_value);
+
+void GDAPI godot_packed_vector3_array_sort(godot_packed_vector3_array *p_self);
+
void GDAPI godot_packed_vector3_array_invert(godot_packed_vector3_array *p_self);
void GDAPI godot_packed_vector3_array_push_back(godot_packed_vector3_array *p_self, const godot_vector3 *p_data);
@@ -310,12 +446,19 @@ void GDAPI godot_packed_color_array_new(godot_packed_color_array *r_dest);
void GDAPI godot_packed_color_array_new_copy(godot_packed_color_array *r_dest, const godot_packed_color_array *p_src);
void GDAPI godot_packed_color_array_new_with_array(godot_packed_color_array *r_dest, const godot_array *p_a);
+const godot_color GDAPI *godot_packed_color_array_ptr(const godot_packed_color_array *p_self);
+godot_color GDAPI *godot_packed_color_array_ptrw(godot_packed_color_array *p_self);
+
void GDAPI godot_packed_color_array_append(godot_packed_color_array *p_self, const godot_color *p_data);
void GDAPI godot_packed_color_array_append_array(godot_packed_color_array *p_self, const godot_packed_color_array *p_array);
godot_error GDAPI godot_packed_color_array_insert(godot_packed_color_array *p_self, const godot_int p_idx, const godot_color *p_data);
+godot_bool GDAPI godot_packed_color_array_has(godot_packed_color_array *p_self, const godot_color *p_value);
+
+void GDAPI godot_packed_color_array_sort(godot_packed_color_array *p_self);
+
void GDAPI godot_packed_color_array_invert(godot_packed_color_array *p_self);
void GDAPI godot_packed_color_array_push_back(godot_packed_color_array *p_self, const godot_color *p_data);
diff --git a/modules/gdnative/include/gdnative/plane.h b/modules/gdnative/include/gdnative/plane.h
index b759a8cc1a..9843056489 100644
--- a/modules/gdnative/include/gdnative/plane.h
+++ b/modules/gdnative/include/gdnative/plane.h
@@ -68,8 +68,6 @@ godot_plane GDAPI godot_plane_normalized(const godot_plane *p_self);
godot_vector3 GDAPI godot_plane_center(const godot_plane *p_self);
-godot_vector3 GDAPI godot_plane_get_any_point(const godot_plane *p_self);
-
godot_bool GDAPI godot_plane_is_point_over(const godot_plane *p_self, const godot_vector3 *p_point);
godot_real GDAPI godot_plane_distance_to(const godot_plane *p_self, const godot_vector3 *p_point);
diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h
index 0ecf072471..f317afc9da 100644
--- a/modules/gdnative/include/gdnative/rect2.h
+++ b/modules/gdnative/include/gdnative/rect2.h
@@ -44,6 +44,13 @@ typedef struct godot_rect2 {
} godot_rect2;
#endif
+#ifndef GODOT_CORE_API_GODOT_RECT2I_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_RECT2I_TYPE_DEFINED
+typedef struct godot_rect2i {
+ uint8_t _dont_touch_that[16];
+} godot_rect2i;
+#endif
+
// reduce extern "C" nesting for VS2013
#ifdef __cplusplus
}
@@ -56,11 +63,15 @@ typedef struct godot_rect2 {
extern "C" {
#endif
+// Rect2
+
void GDAPI godot_rect2_new_with_position_and_size(godot_rect2 *r_dest, const godot_vector2 *p_pos, const godot_vector2 *p_size);
void GDAPI godot_rect2_new(godot_rect2 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_width, const godot_real p_height);
godot_string GDAPI godot_rect2_as_string(const godot_rect2 *p_self);
+godot_rect2i GDAPI godot_rect2_as_rect2i(const godot_rect2 *p_self);
+
godot_real GDAPI godot_rect2_get_area(const godot_rect2 *p_self);
godot_bool GDAPI godot_rect2_intersects(const godot_rect2 *p_self, const godot_rect2 *p_b);
@@ -95,6 +106,49 @@ void GDAPI godot_rect2_set_position(godot_rect2 *p_self, const godot_vector2 *p_
void GDAPI godot_rect2_set_size(godot_rect2 *p_self, const godot_vector2 *p_size);
+// Rect2I
+
+void GDAPI godot_rect2i_new_with_position_and_size(godot_rect2i *r_dest, const godot_vector2i *p_pos, const godot_vector2i *p_size);
+void GDAPI godot_rect2i_new(godot_rect2i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_width, const godot_int p_height);
+
+godot_string GDAPI godot_rect2i_as_string(const godot_rect2i *p_self);
+
+godot_rect2 GDAPI godot_rect2i_as_rect2(const godot_rect2i *p_self);
+
+godot_int GDAPI godot_rect2i_get_area(const godot_rect2i *p_self);
+
+godot_bool GDAPI godot_rect2i_intersects(const godot_rect2i *p_self, const godot_rect2i *p_b);
+
+godot_bool GDAPI godot_rect2i_encloses(const godot_rect2i *p_self, const godot_rect2i *p_b);
+
+godot_bool GDAPI godot_rect2i_has_no_area(const godot_rect2i *p_self);
+
+godot_rect2i GDAPI godot_rect2i_clip(const godot_rect2i *p_self, const godot_rect2i *p_b);
+
+godot_rect2i GDAPI godot_rect2i_merge(const godot_rect2i *p_self, const godot_rect2i *p_b);
+
+godot_bool GDAPI godot_rect2i_has_point(const godot_rect2i *p_self, const godot_vector2i *p_point);
+
+godot_rect2i GDAPI godot_rect2i_grow(const godot_rect2i *p_self, const godot_int p_by);
+
+godot_rect2i GDAPI godot_rect2i_grow_individual(const godot_rect2i *p_self, const godot_int p_left, const godot_int p_top, const godot_int p_right, const godot_int p_bottom);
+
+godot_rect2i GDAPI godot_rect2i_grow_margin(const godot_rect2i *p_self, const godot_int p_margin, const godot_int p_by);
+
+godot_rect2i GDAPI godot_rect2i_abs(const godot_rect2i *p_self);
+
+godot_rect2i GDAPI godot_rect2i_expand(const godot_rect2i *p_self, const godot_vector2i *p_to);
+
+godot_bool GDAPI godot_rect2i_operator_equal(const godot_rect2i *p_self, const godot_rect2i *p_b);
+
+godot_vector2i GDAPI godot_rect2i_get_position(const godot_rect2i *p_self);
+
+godot_vector2i GDAPI godot_rect2i_get_size(const godot_rect2i *p_self);
+
+void GDAPI godot_rect2i_set_position(godot_rect2i *p_self, const godot_vector2i *p_pos);
+
+void GDAPI godot_rect2i_set_size(godot_rect2i *p_self, const godot_vector2i *p_size);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/rid.h b/modules/gdnative/include/gdnative/rid.h
index 04661cedc8..73b601dc04 100644
--- a/modules/gdnative/include/gdnative/rid.h
+++ b/modules/gdnative/include/gdnative/rid.h
@@ -37,7 +37,7 @@ extern "C" {
#include <stdint.h>
-#define GODOT_RID_SIZE sizeof(void *)
+#define GODOT_RID_SIZE sizeof(uint64_t)
#ifndef GODOT_CORE_API_GODOT_RID_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_RID_TYPE_DEFINED
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index 608978db76..0582d95f63 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -38,10 +38,11 @@ extern "C" {
#include <stdint.h>
#include <wchar.h>
-typedef wchar_t godot_char_type;
+typedef char32_t godot_char_type;
#define GODOT_STRING_SIZE sizeof(void *)
#define GODOT_CHAR_STRING_SIZE sizeof(void *)
+#define GODOT_CHAR16_STRING_SIZE sizeof(void *)
#ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED
@@ -58,6 +59,13 @@ typedef struct {
} godot_char_string;
#endif
+#ifndef GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_CHAR16_STRING_SIZE];
+} godot_char16_string;
+#endif
+
// reduce extern "C" nesting for VS2013
#ifdef __cplusplus
}
@@ -75,13 +83,28 @@ godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs);
const char GDAPI *godot_char_string_get_data(const godot_char_string *p_cs);
void GDAPI godot_char_string_destroy(godot_char_string *p_cs);
+godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs);
+const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs);
+void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs);
+
void GDAPI godot_string_new(godot_string *r_dest);
void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src);
-void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size);
-const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx);
-wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx);
-const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self);
+void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents);
+void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents);
+void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents);
+void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents);
+void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents);
+
+void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size);
+void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size);
+void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size);
+void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size);
+void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size);
+
+const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx);
+godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx);
+const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self);
godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b);
godot_bool GDAPI godot_string_operator_less(const godot_string *p_self, const godot_string *p_b);
@@ -89,7 +112,7 @@ godot_string GDAPI godot_string_operator_plus(const godot_string *p_self, const
/* Standard size stuff */
-godot_int GDAPI godot_string_length(const godot_string *p_self);
+/*+++*/ godot_int GDAPI godot_string_length(const godot_string *p_self);
/* Helpers */
@@ -99,25 +122,25 @@ signed char GDAPI godot_string_naturalnocasecmp_to(const godot_string *p_self, c
godot_bool GDAPI godot_string_begins_with(const godot_string *p_self, const godot_string *p_string);
godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, const char *p_char_array);
-godot_array GDAPI godot_string_bigrams(const godot_string *p_self);
-godot_string GDAPI godot_string_chr(wchar_t p_character);
+godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self);
+godot_string GDAPI godot_string_chr(godot_char_type p_character);
godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_string *p_string);
-godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to);
-godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to);
-godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
-godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys);
-godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from);
-godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key);
-godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
-godot_int GDAPI godot_string_find_last(const godot_string *p_self, godot_string p_what);
+godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array);
+godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to);
+godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to);
+godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
+godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys);
+godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from);
+godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key);
+godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values);
godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder);
godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len);
godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self);
-godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self);
-godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string);
+godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self);
+godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string);
godot_bool GDAPI godot_string_is_numeric(const godot_string *p_self);
godot_bool GDAPI godot_string_is_subsequence_of(const godot_string *p_self, const godot_string *p_string);
godot_bool GDAPI godot_string_is_subsequence_ofi(const godot_string *p_self, const godot_string *p_string);
@@ -134,83 +157,97 @@ godot_string GDAPI godot_string_num_scientific(double p_num);
godot_string GDAPI godot_string_num_with_decimals(double p_num, godot_int p_decimals);
godot_string GDAPI godot_string_pad_decimals(const godot_string *p_self, godot_int p_digits);
godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int p_digits);
-godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with);
-godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with);
-godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with);
-godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
-godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
+godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
+godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
+godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
+godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
+godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
godot_string GDAPI godot_string_rpad(const godot_string *p_self, godot_int p_min_length);
godot_string GDAPI godot_string_rpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character);
godot_real GDAPI godot_string_similarity(const godot_string *p_self, const godot_string *p_string);
godot_string GDAPI godot_string_sprintf(const godot_string *p_self, const godot_array *p_values, godot_bool *p_error);
godot_string GDAPI godot_string_substr(const godot_string *p_self, godot_int p_from, godot_int p_chars);
-double GDAPI godot_string_to_double(const godot_string *p_self);
-godot_real GDAPI godot_string_to_float(const godot_string *p_self);
+double GDAPI godot_string_to_float(const godot_string *p_self);
godot_int GDAPI godot_string_to_int(const godot_string *p_self);
godot_string GDAPI godot_string_camelcase_to_underscore(const godot_string *p_self);
godot_string GDAPI godot_string_camelcase_to_underscore_lowercased(const godot_string *p_self);
godot_string GDAPI godot_string_capitalize(const godot_string *p_self);
-double GDAPI godot_string_char_to_double(const char *p_what);
+
+double GDAPI godot_string_char_to_float(const char *p_what);
+double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end);
+
godot_int GDAPI godot_string_char_to_int(const char *p_what);
-int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str);
+godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str);
+
godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int p_len);
-int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len);
-int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self);
-int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self);
-int64_t GDAPI godot_string_to_int64(const godot_string *p_self);
-double GDAPI godot_string_unicode_char_to_double(const wchar_t *p_str, const wchar_t **r_end);
-
-godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter);
-godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice);
-godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice);
-
-godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_spaces(const godot_string *p_self);
-
-wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char);
-wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char);
+godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len);
+
+godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter);
+godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice);
+godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice);
+
+godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit);
+
+godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit);
+
+godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+
+godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self);
+
+godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char);
+godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char);
godot_string GDAPI godot_string_to_lower(const godot_string *p_self);
godot_string GDAPI godot_string_to_upper(const godot_string *p_self);
godot_string GDAPI godot_string_get_basename(const godot_string *p_self);
godot_string GDAPI godot_string_get_extension(const godot_string *p_self);
godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos);
-wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx);
+godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx);
godot_string GDAPI godot_string_plus_file(const godot_string *p_self, const godot_string *p_file);
godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_pos);
+godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count);
godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right);
godot_string GDAPI godot_string_strip_escapes(const godot_string *p_self);
void GDAPI godot_string_erase(godot_string *p_self, godot_int p_pos, godot_int p_chars);
godot_char_string GDAPI godot_string_ascii(const godot_string *p_self);
-godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self);
+godot_char_string GDAPI godot_string_latin1(const godot_string *p_self);
+
godot_char_string GDAPI godot_string_utf8(const godot_string *p_self);
godot_bool GDAPI godot_string_parse_utf8(godot_string *p_self, const char *p_utf8);
godot_bool GDAPI godot_string_parse_utf8_with_len(godot_string *p_self, const char *p_utf8, godot_int p_len);
-godot_string GDAPI godot_string_chars_to_utf8(const char *p_utf8);
-godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot_int p_len);
+
+godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self);
+godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16);
+godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len);
uint32_t GDAPI godot_string_hash(const godot_string *p_self);
uint64_t GDAPI godot_string_hash64(const godot_string *p_self);
+
uint32_t GDAPI godot_string_hash_chars(const char *p_cstr);
uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_len);
-uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str);
-uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len);
+uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str);
+uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len);
+
godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self);
godot_string GDAPI godot_string_md5_text(const godot_string *p_self);
+godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self);
+godot_string GDAPI godot_string_sha1_text(const godot_string *p_self);
godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self);
godot_string GDAPI godot_string_sha256_text(const godot_string *p_self);
@@ -233,14 +270,15 @@ godot_string GDAPI godot_string_c_unescape(const godot_string *p_self);
godot_string GDAPI godot_string_http_escape(const godot_string *p_self);
godot_string GDAPI godot_string_http_unescape(const godot_string *p_self);
godot_string GDAPI godot_string_json_escape(const godot_string *p_self);
-godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line);
godot_string GDAPI godot_string_xml_escape(const godot_string *p_self);
godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self);
godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self);
godot_string GDAPI godot_string_percent_decode(const godot_string *p_self);
godot_string GDAPI godot_string_percent_encode(const godot_string *p_self);
+godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts);
+godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix);
godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self);
@@ -251,8 +289,8 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self);
godot_string GDAPI godot_string_dedent(const godot_string *p_self);
godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix);
godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix);
+godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars);
godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars);
-godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit);
void GDAPI godot_string_destroy(godot_string *p_self);
diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h
index 934e856fbf..0a611b76e9 100644
--- a/modules/gdnative/include/gdnative/variant.h
+++ b/modules/gdnative/include/gdnative/variant.h
@@ -37,7 +37,7 @@ extern "C" {
#include <stdint.h>
-#define GODOT_VARIANT_SIZE (16 + sizeof(void *))
+#define GODOT_VARIANT_SIZE (16 + sizeof(int64_t))
#ifndef GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED
@@ -71,16 +71,21 @@ typedef enum godot_variant_type {
// misc types
GODOT_VARIANT_TYPE_COLOR,
+ GODOT_VARIANT_TYPE_STRING_NAME,
GODOT_VARIANT_TYPE_NODE_PATH,
GODOT_VARIANT_TYPE_RID,
GODOT_VARIANT_TYPE_OBJECT,
+ GODOT_VARIANT_TYPE_CALLABLE,
+ GODOT_VARIANT_TYPE_SIGNAL,
GODOT_VARIANT_TYPE_DICTIONARY,
GODOT_VARIANT_TYPE_ARRAY,
// arrays
GODOT_VARIANT_TYPE_PACKED_BYTE_ARRAY,
- GODOT_VARIANT_TYPE_PACKED_INT_ARRAY,
- GODOT_VARIANT_TYPE_PACKED_REAL_ARRAY,
+ GODOT_VARIANT_TYPE_PACKED_INT32_ARRAY,
+ GODOT_VARIANT_TYPE_PACKED_INT64_ARRAY,
+ GODOT_VARIANT_TYPE_PACKED_FLOAT32_ARRAY,
+ GODOT_VARIANT_TYPE_PACKED_FLOAT64_ARRAY,
GODOT_VARIANT_TYPE_PACKED_STRING_ARRAY,
GODOT_VARIANT_TYPE_PACKED_VECTOR2_ARRAY,
GODOT_VARIANT_TYPE_PACKED_VECTOR3_ARRAY,
@@ -149,15 +154,17 @@ typedef enum godot_variant_operator {
#include <gdnative/aabb.h>
#include <gdnative/array.h>
#include <gdnative/basis.h>
+#include <gdnative/callable.h>
#include <gdnative/color.h>
#include <gdnative/dictionary.h>
#include <gdnative/node_path.h>
+#include <gdnative/packed_arrays.h>
#include <gdnative/plane.h>
-#include <gdnative/pool_arrays.h>
#include <gdnative/quat.h>
#include <gdnative/rect2.h>
#include <gdnative/rid.h>
#include <gdnative/string.h>
+#include <gdnative/string_name.h>
#include <gdnative/transform.h>
#include <gdnative/transform2d.h>
#include <gdnative/variant.h>
@@ -181,9 +188,13 @@ void GDAPI godot_variant_new_uint(godot_variant *r_dest, const uint64_t p_i);
void GDAPI godot_variant_new_int(godot_variant *r_dest, const int64_t p_i);
void GDAPI godot_variant_new_real(godot_variant *r_dest, const double p_r);
void GDAPI godot_variant_new_string(godot_variant *r_dest, const godot_string *p_s);
+void GDAPI godot_variant_new_string_name(godot_variant *r_dest, const godot_string_name *p_s);
void GDAPI godot_variant_new_vector2(godot_variant *r_dest, const godot_vector2 *p_v2);
+void GDAPI godot_variant_new_vector2i(godot_variant *r_dest, const godot_vector2i *p_v2);
void GDAPI godot_variant_new_rect2(godot_variant *r_dest, const godot_rect2 *p_rect2);
+void GDAPI godot_variant_new_rect2i(godot_variant *r_dest, const godot_rect2i *p_rect2);
void GDAPI godot_variant_new_vector3(godot_variant *r_dest, const godot_vector3 *p_v3);
+void GDAPI godot_variant_new_vector3i(godot_variant *r_dest, const godot_vector3i *p_v3);
void GDAPI godot_variant_new_transform2d(godot_variant *r_dest, const godot_transform2d *p_t2d);
void GDAPI godot_variant_new_plane(godot_variant *r_dest, const godot_plane *p_plane);
void GDAPI godot_variant_new_quat(godot_variant *r_dest, const godot_quat *p_quat);
@@ -193,12 +204,16 @@ void GDAPI godot_variant_new_transform(godot_variant *r_dest, const godot_transf
void GDAPI godot_variant_new_color(godot_variant *r_dest, const godot_color *p_color);
void GDAPI godot_variant_new_node_path(godot_variant *r_dest, const godot_node_path *p_np);
void GDAPI godot_variant_new_rid(godot_variant *r_dest, const godot_rid *p_rid);
+void GDAPI godot_variant_new_callable(godot_variant *r_dest, const godot_callable *p_callable);
+void GDAPI godot_variant_new_signal(godot_variant *r_dest, const godot_signal *p_signal);
void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p_obj);
void GDAPI godot_variant_new_dictionary(godot_variant *r_dest, const godot_dictionary *p_dict);
void GDAPI godot_variant_new_array(godot_variant *r_dest, const godot_array *p_arr);
void GDAPI godot_variant_new_packed_byte_array(godot_variant *r_dest, const godot_packed_byte_array *p_pba);
-void GDAPI godot_variant_new_packed_int_array(godot_variant *r_dest, const godot_packed_int_array *p_pia);
-void GDAPI godot_variant_new_packed_real_array(godot_variant *r_dest, const godot_packed_real_array *p_pra);
+void GDAPI godot_variant_new_packed_int32_array(godot_variant *r_dest, const godot_packed_int32_array *p_pia);
+void GDAPI godot_variant_new_packed_int64_array(godot_variant *r_dest, const godot_packed_int64_array *p_pia);
+void GDAPI godot_variant_new_packed_float32_array(godot_variant *r_dest, const godot_packed_float32_array *p_pra);
+void GDAPI godot_variant_new_packed_float64_array(godot_variant *r_dest, const godot_packed_float64_array *p_pra);
void GDAPI godot_variant_new_packed_string_array(godot_variant *r_dest, const godot_packed_string_array *p_psa);
void GDAPI godot_variant_new_packed_vector2_array(godot_variant *r_dest, const godot_packed_vector2_array *p_pv2a);
void GDAPI godot_variant_new_packed_vector3_array(godot_variant *r_dest, const godot_packed_vector3_array *p_pv3a);
@@ -209,9 +224,13 @@ uint64_t GDAPI godot_variant_as_uint(const godot_variant *p_self);
int64_t GDAPI godot_variant_as_int(const godot_variant *p_self);
double GDAPI godot_variant_as_real(const godot_variant *p_self);
godot_string GDAPI godot_variant_as_string(const godot_variant *p_self);
+godot_string_name GDAPI godot_variant_as_string_name(const godot_variant *p_self);
godot_vector2 GDAPI godot_variant_as_vector2(const godot_variant *p_self);
+godot_vector2i GDAPI godot_variant_as_vector2i(const godot_variant *p_self);
godot_rect2 GDAPI godot_variant_as_rect2(const godot_variant *p_self);
+godot_rect2i GDAPI godot_variant_as_rect2i(const godot_variant *p_self);
godot_vector3 GDAPI godot_variant_as_vector3(const godot_variant *p_self);
+godot_vector3i GDAPI godot_variant_as_vector3i(const godot_variant *p_self);
godot_transform2d GDAPI godot_variant_as_transform2d(const godot_variant *p_self);
godot_plane GDAPI godot_variant_as_plane(const godot_variant *p_self);
godot_quat GDAPI godot_variant_as_quat(const godot_variant *p_self);
@@ -221,12 +240,16 @@ godot_transform GDAPI godot_variant_as_transform(const godot_variant *p_self);
godot_color GDAPI godot_variant_as_color(const godot_variant *p_self);
godot_node_path GDAPI godot_variant_as_node_path(const godot_variant *p_self);
godot_rid GDAPI godot_variant_as_rid(const godot_variant *p_self);
+godot_callable GDAPI godot_variant_as_callable(const godot_variant *p_self);
+godot_signal GDAPI godot_variant_as_signal(const godot_variant *p_self);
godot_object GDAPI *godot_variant_as_object(const godot_variant *p_self);
godot_dictionary GDAPI godot_variant_as_dictionary(const godot_variant *p_self);
godot_array GDAPI godot_variant_as_array(const godot_variant *p_self);
godot_packed_byte_array GDAPI godot_variant_as_packed_byte_array(const godot_variant *p_self);
-godot_packed_int_array GDAPI godot_variant_as_packed_int_array(const godot_variant *p_self);
-godot_packed_real_array GDAPI godot_variant_as_packed_real_array(const godot_variant *p_self);
+godot_packed_int32_array GDAPI godot_variant_as_packed_int32_array(const godot_variant *p_self);
+godot_packed_int64_array GDAPI godot_variant_as_packed_int64_array(const godot_variant *p_self);
+godot_packed_float32_array GDAPI godot_variant_as_packed_float32_array(const godot_variant *p_self);
+godot_packed_float64_array GDAPI godot_variant_as_packed_float64_array(const godot_variant *p_self);
godot_packed_string_array GDAPI godot_variant_as_packed_string_array(const godot_variant *p_self);
godot_packed_vector2_array GDAPI godot_variant_as_packed_vector2_array(const godot_variant *p_self);
godot_packed_vector3_array GDAPI godot_variant_as_packed_vector3_array(const godot_variant *p_self);
@@ -239,6 +262,7 @@ godot_bool GDAPI godot_variant_has_method(const godot_variant *p_self, const god
godot_bool GDAPI godot_variant_operator_equal(const godot_variant *p_self, const godot_variant *p_other);
godot_bool GDAPI godot_variant_operator_less(const godot_variant *p_self, const godot_variant *p_other);
+uint32_t GDAPI godot_variant_hash(const godot_variant *p_self);
godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const godot_variant *p_other);
godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self);
diff --git a/modules/gdnative/include/gdnative/vector2.h b/modules/gdnative/include/gdnative/vector2.h
index c11e23a586..35b02c5a75 100644
--- a/modules/gdnative/include/gdnative/vector2.h
+++ b/modules/gdnative/include/gdnative/vector2.h
@@ -46,6 +46,15 @@ typedef struct {
} godot_vector2;
#endif
+#define GODOT_VECTOR2I_SIZE 8
+
+#ifndef GODOT_CORE_API_GODOT_VECTOR2I_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_VECTOR2I_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR2I_SIZE];
+} godot_vector2i;
+#endif
+
// reduce extern "C" nesting for VS2013
#ifdef __cplusplus
}
@@ -57,10 +66,14 @@ typedef struct {
extern "C" {
#endif
+// Vector2
+
void GDAPI godot_vector2_new(godot_vector2 *r_dest, const godot_real p_x, const godot_real p_y);
godot_string GDAPI godot_vector2_as_string(const godot_vector2 *p_self);
+godot_vector2i GDAPI godot_vector2_as_vector2i(const godot_vector2 *p_self);
+
godot_vector2 GDAPI godot_vector2_normalized(const godot_vector2 *p_self);
godot_real GDAPI godot_vector2_length(const godot_vector2 *p_self);
@@ -93,6 +106,8 @@ godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self);
godot_vector2 GDAPI godot_vector2_floor(const godot_vector2 *p_self);
+godot_vector2 GDAPI godot_vector2_sign(const godot_vector2 *p_self);
+
godot_vector2 GDAPI godot_vector2_snapped(const godot_vector2 *p_self, const godot_vector2 *p_by);
godot_real GDAPI godot_vector2_aspect(const godot_vector2 *p_self);
@@ -135,6 +150,46 @@ godot_real GDAPI godot_vector2_get_x(const godot_vector2 *p_self);
godot_real GDAPI godot_vector2_get_y(const godot_vector2 *p_self);
+// Vector2i
+
+void GDAPI godot_vector2i_new(godot_vector2i *r_dest, const godot_int p_x, const godot_int p_y);
+
+godot_string GDAPI godot_vector2i_as_string(const godot_vector2i *p_self);
+
+godot_vector2 GDAPI godot_vector2i_as_vector2(const godot_vector2i *p_self);
+
+godot_real GDAPI godot_vector2i_aspect(const godot_vector2i *p_self);
+
+godot_vector2i GDAPI godot_vector2i_abs(const godot_vector2i *p_self);
+
+godot_vector2i GDAPI godot_vector2i_sign(const godot_vector2i *p_self);
+
+godot_vector2i GDAPI godot_vector2i_operator_add(const godot_vector2i *p_self, const godot_vector2i *p_b);
+
+godot_vector2i GDAPI godot_vector2i_operator_subtract(const godot_vector2i *p_self, const godot_vector2i *p_b);
+
+godot_vector2i GDAPI godot_vector2i_operator_multiply_vector(const godot_vector2i *p_self, const godot_vector2i *p_b);
+
+godot_vector2i GDAPI godot_vector2i_operator_multiply_scalar(const godot_vector2i *p_self, const godot_int p_b);
+
+godot_vector2i GDAPI godot_vector2i_operator_divide_vector(const godot_vector2i *p_self, const godot_vector2i *p_b);
+
+godot_vector2i GDAPI godot_vector2i_operator_divide_scalar(const godot_vector2i *p_self, const godot_int p_b);
+
+godot_bool GDAPI godot_vector2i_operator_equal(const godot_vector2i *p_self, const godot_vector2i *p_b);
+
+godot_bool GDAPI godot_vector2i_operator_less(const godot_vector2i *p_self, const godot_vector2i *p_b);
+
+godot_vector2i GDAPI godot_vector2i_operator_neg(const godot_vector2i *p_self);
+
+void GDAPI godot_vector2i_set_x(godot_vector2i *p_self, const godot_int p_x);
+
+void GDAPI godot_vector2i_set_y(godot_vector2i *p_self, const godot_int p_y);
+
+godot_int GDAPI godot_vector2i_get_x(const godot_vector2i *p_self);
+
+godot_int GDAPI godot_vector2i_get_y(const godot_vector2i *p_self);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/vector3.h b/modules/gdnative/include/gdnative/vector3.h
index 8ebf15b724..5127b8789b 100644
--- a/modules/gdnative/include/gdnative/vector3.h
+++ b/modules/gdnative/include/gdnative/vector3.h
@@ -46,6 +46,15 @@ typedef struct {
} godot_vector3;
#endif
+#define GODOT_VECTOR3I_SIZE 12
+
+#ifndef GODOT_CORE_API_GODOT_VECTOR3I_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_VECTOR3I_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR3I_SIZE];
+} godot_vector3i;
+#endif
+
// reduce extern "C" nesting for VS2013
#ifdef __cplusplus
}
@@ -64,10 +73,14 @@ typedef enum {
GODOT_VECTOR3_AXIS_Z,
} godot_vector3_axis;
+// Vector3
+
void GDAPI godot_vector3_new(godot_vector3 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z);
godot_string GDAPI godot_vector3_as_string(const godot_vector3 *p_self);
+godot_vector3i GDAPI godot_vector3_as_vector3i(const godot_vector3 *p_self);
+
godot_int GDAPI godot_vector3_min_axis(const godot_vector3 *p_self);
godot_int GDAPI godot_vector3_max_axis(const godot_vector3 *p_self);
@@ -102,6 +115,8 @@ godot_basis GDAPI godot_vector3_to_diagonal_matrix(const godot_vector3 *p_self);
godot_vector3 GDAPI godot_vector3_abs(const godot_vector3 *p_self);
+godot_vector3 GDAPI godot_vector3_sign(const godot_vector3 *p_self);
+
godot_vector3 GDAPI godot_vector3_floor(const godot_vector3 *p_self);
godot_vector3 GDAPI godot_vector3_ceil(const godot_vector3 *p_self);
@@ -142,6 +157,44 @@ void GDAPI godot_vector3_set_axis(godot_vector3 *p_self, const godot_vector3_axi
godot_real GDAPI godot_vector3_get_axis(const godot_vector3 *p_self, const godot_vector3_axis p_axis);
+// Vector3i
+
+void GDAPI godot_vector3i_new(godot_vector3i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_z);
+
+godot_string GDAPI godot_vector3i_as_string(const godot_vector3i *p_self);
+
+godot_vector3 GDAPI godot_vector3i_as_vector3(const godot_vector3i *p_self);
+
+godot_int GDAPI godot_vector3i_min_axis(const godot_vector3i *p_self);
+
+godot_int GDAPI godot_vector3i_max_axis(const godot_vector3i *p_self);
+
+godot_vector3i GDAPI godot_vector3i_abs(const godot_vector3i *p_self);
+
+godot_vector3i GDAPI godot_vector3i_sign(const godot_vector3i *p_self);
+
+godot_vector3i GDAPI godot_vector3i_operator_add(const godot_vector3i *p_self, const godot_vector3i *p_b);
+
+godot_vector3i GDAPI godot_vector3i_operator_subtract(const godot_vector3i *p_self, const godot_vector3i *p_b);
+
+godot_vector3i GDAPI godot_vector3i_operator_multiply_vector(const godot_vector3i *p_self, const godot_vector3i *p_b);
+
+godot_vector3i GDAPI godot_vector3i_operator_multiply_scalar(const godot_vector3i *p_self, const godot_int p_b);
+
+godot_vector3i GDAPI godot_vector3i_operator_divide_vector(const godot_vector3i *p_self, const godot_vector3i *p_b);
+
+godot_vector3i GDAPI godot_vector3i_operator_divide_scalar(const godot_vector3i *p_self, const godot_int p_b);
+
+godot_bool GDAPI godot_vector3i_operator_equal(const godot_vector3i *p_self, const godot_vector3i *p_b);
+
+godot_bool GDAPI godot_vector3i_operator_less(const godot_vector3i *p_self, const godot_vector3i *p_b);
+
+godot_vector3i GDAPI godot_vector3i_operator_neg(const godot_vector3i *p_self);
+
+void GDAPI godot_vector3i_set_axis(godot_vector3i *p_self, const godot_vector3_axis p_axis, const godot_int p_val);
+
+godot_int GDAPI godot_vector3i_get_axis(const godot_vector3i *p_self, const godot_vector3_axis p_axis);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index 0fb5180103..825033c99c 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -45,7 +45,7 @@ typedef enum {
GODOT_METHOD_RPC_MODE_REMOTESYNC,
GODOT_METHOD_RPC_MODE_MASTERSYNC,
GODOT_METHOD_RPC_MODE_PUPPETSYNC,
-} godot_method_rpc_mode;
+} godot_nativescript_method_rpc_mode;
typedef enum {
GODOT_PROPERTY_HINT_NONE, ///< no hint provided.
@@ -82,7 +82,7 @@ typedef enum {
GODOT_PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance
GODOT_PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base
GODOT_PROPERTY_HINT_MAX,
-} godot_property_hint;
+} godot_nativescript_property_hint;
typedef enum {
@@ -106,106 +106,95 @@ typedef enum {
GODOT_PROPERTY_USAGE_DEFAULT = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_EDITOR | GODOT_PROPERTY_USAGE_NETWORK,
GODOT_PROPERTY_USAGE_DEFAULT_INTL = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_EDITOR | GODOT_PROPERTY_USAGE_NETWORK | GODOT_PROPERTY_USAGE_INTERNATIONALIZED,
GODOT_PROPERTY_USAGE_NOEDITOR = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_NETWORK,
-} godot_property_usage_flags;
+} godot_nativescript_property_usage_flags;
typedef struct {
- godot_method_rpc_mode rset_type;
+ godot_nativescript_method_rpc_mode rset_type;
godot_int type;
- godot_property_hint hint;
+ godot_nativescript_property_hint hint;
godot_string hint_string;
- godot_property_usage_flags usage;
+ godot_nativescript_property_usage_flags usage;
godot_variant default_value;
-} godot_property_attributes;
+} godot_nativescript_property_attributes;
typedef struct {
// instance pointer, method_data - return user data
GDCALLINGCONV void *(*create_func)(godot_object *, void *);
void *method_data;
GDCALLINGCONV void (*free_func)(void *);
-} godot_instance_create_func;
+} godot_nativescript_instance_create_func;
typedef struct {
// instance pointer, method data, user data
GDCALLINGCONV void (*destroy_func)(godot_object *, void *, void *);
void *method_data;
GDCALLINGCONV void (*free_func)(void *);
-} godot_instance_destroy_func;
+} godot_nativescript_instance_destroy_func;
-void GDAPI godot_nativescript_register_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_instance_create_func p_create_func, godot_instance_destroy_func p_destroy_func);
+void GDAPI godot_nativescript_register_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_nativescript_instance_create_func p_create_func, godot_nativescript_instance_destroy_func p_destroy_func);
-void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_instance_create_func p_create_func, godot_instance_destroy_func p_destroy_func);
+void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_nativescript_instance_create_func p_create_func, godot_nativescript_instance_destroy_func p_destroy_func);
typedef struct {
- godot_method_rpc_mode rpc_type;
-} godot_method_attributes;
+ godot_nativescript_method_rpc_mode rpc_type;
+} godot_nativescript_method_attributes;
+
+typedef struct {
+ godot_string name;
+
+ godot_variant_type type;
+ godot_nativescript_property_hint hint;
+ godot_string hint_string;
+} godot_nativescript_method_argument;
typedef struct {
// instance pointer, method data, user data, num args, args - return result as varaint
GDCALLINGCONV godot_variant (*method)(godot_object *, void *, void *, int, godot_variant **);
void *method_data;
GDCALLINGCONV void (*free_func)(void *);
-} godot_instance_method;
+} godot_nativescript_instance_method;
-void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const char *p_name, const char *p_function_name, godot_method_attributes p_attr, godot_instance_method p_method);
+void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const char *p_name, const char *p_function_name, godot_nativescript_method_attributes p_attr, godot_nativescript_instance_method p_method);
+void GDAPI godot_nativescript_set_method_argument_information(void *p_gdnative_handle, const char *p_name, const char *p_function_name, int p_num_args, const godot_nativescript_method_argument *p_args);
typedef struct {
// instance pointer, method data, user data, value
GDCALLINGCONV void (*set_func)(godot_object *, void *, void *, godot_variant *);
void *method_data;
GDCALLINGCONV void (*free_func)(void *);
-} godot_property_set_func;
+} godot_nativescript_property_set_func;
typedef struct {
// instance pointer, method data, user data, value
GDCALLINGCONV godot_variant (*get_func)(godot_object *, void *, void *);
void *method_data;
GDCALLINGCONV void (*free_func)(void *);
-} godot_property_get_func;
+} godot_nativescript_property_get_func;
-void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const char *p_name, const char *p_path, godot_property_attributes *p_attr, godot_property_set_func p_set_func, godot_property_get_func p_get_func);
+void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const char *p_name, const char *p_path, godot_nativescript_property_attributes *p_attr, godot_nativescript_property_set_func p_set_func, godot_nativescript_property_get_func p_get_func);
typedef struct {
godot_string name;
godot_int type;
- godot_property_hint hint;
+ godot_nativescript_property_hint hint;
godot_string hint_string;
- godot_property_usage_flags usage;
+ godot_nativescript_property_usage_flags usage;
godot_variant default_value;
-} godot_signal_argument;
+} godot_nativescript_signal_argument;
typedef struct {
godot_string name;
int num_args;
- godot_signal_argument *args;
+ godot_nativescript_signal_argument *args;
int num_default_args;
godot_variant *default_args;
-} godot_signal;
+} godot_nativescript_signal;
-void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const char *p_name, const godot_signal *p_signal);
+void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const char *p_name, const godot_nativescript_signal *p_signal);
void GDAPI *godot_nativescript_get_userdata(godot_object *p_instance);
-/*
- *
- *
- * NativeScript 1.1
- *
- *
- */
-
-// method registering with argument names
-
-typedef struct {
- godot_string name;
-
- godot_variant_type type;
- godot_property_hint hint;
- godot_string hint_string;
-} godot_method_arg;
-
-void GDAPI godot_nativescript_set_method_argument_information(void *p_gdnative_handle, const char *p_name, const char *p_function_name, int p_num_args, const godot_method_arg *p_args);
-
// documentation
void GDAPI godot_nativescript_set_class_documentation(void *p_gdnative_handle, const char *p_name, godot_string p_documentation);
@@ -230,9 +219,9 @@ typedef struct {
GDCALLINGCONV bool (*refcount_decremented_instance_binding)(void *, godot_object *);
void *data;
GDCALLINGCONV void (*free_func)(void *);
-} godot_instance_binding_functions;
+} godot_nativescript_instance_binding_functions;
-int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions);
+int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_nativescript_instance_binding_functions p_binding_functions);
void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_idx);
void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object);
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 62f2ec5024..8dbaec4e75 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -139,7 +139,7 @@ static String get_type_name(const PropertyInfo &info) {
}
/*
- * Some comparison helper functions we need
+ * Some comparison helper functions we need
*/
struct MethodInfoComparator {
@@ -176,10 +176,10 @@ List<ClassAPI> generate_c_api_classes() {
// Register global constants as a fake GlobalConstants singleton class
{
ClassAPI global_constants_api;
- global_constants_api.class_name = L"GlobalConstants";
+ global_constants_api.class_name = "GlobalConstants";
global_constants_api.api_type = ClassDB::API_CORE;
global_constants_api.is_singleton = true;
- global_constants_api.singleton_name = L"GlobalConstants";
+ global_constants_api.singleton_name = "GlobalConstants";
global_constants_api.is_instanciable = false;
const int constants_count = GlobalConstants::get_global_constant_count();
for (int i = 0; i < constants_count; ++i) {
@@ -214,7 +214,7 @@ List<ClassAPI> generate_c_api_classes() {
{
List<StringName> inheriters;
ClassDB::get_inheriters_from_class("Reference", &inheriters);
- bool is_reference = !!inheriters.find(class_name);
+ bool is_reference = !!inheriters.find(class_name) || class_name == "Reference";
// @Unclear
class_api.is_reference = !class_api.is_singleton && is_reference;
}
@@ -452,6 +452,7 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back("\t\t\t\t\t{\n");
source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n");
source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n");
+ source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n");
source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n");
source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n");
}
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index 1bdac0dcb2..e47548f3e9 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -51,7 +51,7 @@ extern "C" void _native_script_hook() {
// Script API
-void GDAPI godot_nativescript_register_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_instance_create_func p_create_func, godot_instance_destroy_func p_destroy_func) {
+void GDAPI godot_nativescript_register_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_nativescript_instance_create_func p_create_func, godot_nativescript_instance_destroy_func p_destroy_func) {
String *s = (String *)p_gdnative_handle;
Map<StringName, NativeScriptDesc> *classes = &NSL->library_classes[*s];
@@ -83,7 +83,7 @@ void GDAPI godot_nativescript_register_class(void *p_gdnative_handle, const char
classes->insert(p_name, desc);
}
-void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_instance_create_func p_create_func, godot_instance_destroy_func p_destroy_func) {
+void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const char *p_name, const char *p_base, godot_nativescript_instance_create_func p_create_func, godot_nativescript_instance_destroy_func p_destroy_func) {
String *s = (String *)p_gdnative_handle;
Map<StringName, NativeScriptDesc> *classes = &NSL->library_classes[*s];
@@ -116,7 +116,7 @@ void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const
classes->insert(p_name, desc);
}
-void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const char *p_name, const char *p_function_name, godot_method_attributes p_attr, godot_instance_method p_method) {
+void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const char *p_name, const char *p_function_name, godot_nativescript_method_attributes p_attr, godot_nativescript_instance_method p_method) {
String *s = (String *)p_gdnative_handle;
Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
@@ -135,7 +135,7 @@ void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const cha
E->get().methods.insert(p_function_name, method);
}
-void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const char *p_name, const char *p_path, godot_property_attributes *p_attr, godot_property_set_func p_set_func, godot_property_get_func p_get_func) {
+void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const char *p_name, const char *p_path, godot_nativescript_property_attributes *p_attr, godot_nativescript_property_set_func p_set_func, godot_nativescript_property_get_func p_get_func) {
String *s = (String *)p_gdnative_handle;
Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
@@ -159,7 +159,7 @@ void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const c
E->get().properties.insert(p_path, property);
}
-void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const char *p_name, const godot_signal *p_signal) {
+void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const char *p_name, const godot_nativescript_signal *p_signal) {
String *s = (String *)p_gdnative_handle;
Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
@@ -171,7 +171,7 @@ void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const cha
for (int i = 0; i < p_signal->num_args; i++) {
PropertyInfo info;
- godot_signal_argument arg = p_signal->args[i];
+ godot_nativescript_signal_argument arg = p_signal->args[i];
info.hint = (PropertyHint)arg.hint;
info.hint_string = *(String *)&arg.hint_string;
@@ -184,7 +184,7 @@ void GDAPI godot_nativescript_register_signal(void *p_gdnative_handle, const cha
for (int i = 0; i < p_signal->num_default_args; i++) {
Variant *v;
- godot_signal_argument attrib = p_signal->args[i];
+ godot_nativescript_signal_argument attrib = p_signal->args[i];
v = (Variant *)&attrib.default_value;
@@ -221,7 +221,7 @@ void GDAPI *godot_nativescript_get_userdata(godot_object *p_instance) {
*
*/
-void GDAPI godot_nativescript_set_method_argument_information(void *p_gdnative_handle, const char *p_name, const char *p_function_name, int p_num_args, const godot_method_arg *p_args) {
+void GDAPI godot_nativescript_set_method_argument_information(void *p_gdnative_handle, const char *p_name, const char *p_function_name, int p_num_args, const godot_nativescript_method_argument *p_args) {
String *s = (String *)p_gdnative_handle;
Map<StringName, NativeScriptDesc>::Element *E = NSL->library_classes[*s].find(p_name);
@@ -235,7 +235,7 @@ void GDAPI godot_nativescript_set_method_argument_information(void *p_gdnative_h
List<PropertyInfo> args;
for (int i = 0; i < p_num_args; i++) {
- godot_method_arg arg = p_args[i];
+ godot_nativescript_method_argument arg = p_args[i];
String name = *(String *)&arg.name;
String hint_string = *(String *)&arg.hint_string;
@@ -329,7 +329,7 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object)
return nullptr;
}
-int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions) {
+int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_nativescript_instance_binding_functions p_binding_functions) {
return NativeScriptLanguage::get_singleton()->register_binding_functions(p_binding_functions);
}
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index f3dfd0b68e..632f4e5fee 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -991,7 +991,8 @@ void NativeScriptInstance::notification(int p_notification) {
Variant value = p_notification;
const Variant *args[1] = { &value };
- call_multilevel("_notification", args, 1);
+ Callable::CallError error;
+ call("_notification", args, 1, error);
}
String NativeScriptInstance::to_string(bool *r_valid) {
@@ -1087,31 +1088,6 @@ ScriptLanguage *NativeScriptInstance::get_language() {
return NativeScriptLanguage::get_singleton();
}
-void NativeScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- NativeScriptDesc *script_data = GET_SCRIPT_DESC();
-
- while (script_data) {
- Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
- if (E) {
- godot_variant res = E->get().method.method((godot_object *)owner,
- E->get().method.method_data,
- userdata,
- p_argcount,
- (godot_variant **)p_args);
- godot_variant_destroy(&res);
- }
- script_data = script_data->base_data;
- }
-}
-
-void NativeScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- NativeScriptDesc *script_data = GET_SCRIPT_DESC();
-
- if (script_data) {
- _ml_call_reversed(script_data, p_method, p_args, p_argcount);
- }
-}
-
NativeScriptInstance::~NativeScriptInstance() {
NativeScriptDesc *script_data = GET_SCRIPT_DESC();
@@ -1498,7 +1474,7 @@ void NativeScriptLanguage::profiling_add_data(StringName p_signature, uint64_t p
#endif
}
-int NativeScriptLanguage::register_binding_functions(godot_instance_binding_functions p_binding_functions) {
+int NativeScriptLanguage::register_binding_functions(godot_nativescript_instance_binding_functions p_binding_functions) {
// find index
int idx = -1;
@@ -1853,7 +1829,7 @@ void NativeReloadNode::_notification(int p_what) {
#ifdef TOOLS_ENABLED
switch (p_what) {
- case NOTIFICATION_WM_FOCUS_OUT: {
+ case NOTIFICATION_APPLICATION_FOCUS_OUT: {
if (unloaded) {
break;
}
@@ -1887,7 +1863,7 @@ void NativeReloadNode::_notification(int p_what) {
} break;
- case NOTIFICATION_WM_FOCUS_IN: {
+ case NOTIFICATION_APPLICATION_FOCUS_IN: {
if (!unloaded) {
break;
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 1aca142889..145bf7dcb6 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -48,7 +48,7 @@
struct NativeScriptDesc {
struct Method {
- godot_instance_method method;
+ godot_nativescript_instance_method method;
MethodInfo info;
int rpc_mode;
uint16_t rpc_method_id;
@@ -56,8 +56,8 @@ struct NativeScriptDesc {
};
struct Property {
- godot_property_set_func setter;
- godot_property_get_func getter;
+ godot_nativescript_property_set_func setter;
+ godot_nativescript_property_get_func getter;
PropertyInfo info;
Variant default_value;
int rset_mode;
@@ -78,8 +78,8 @@ struct NativeScriptDesc {
StringName base;
StringName base_native_type;
NativeScriptDesc *base_data;
- godot_instance_create_func create_func;
- godot_instance_destroy_func destroy_func;
+ godot_nativescript_instance_create_func create_func;
+ godot_nativescript_instance_destroy_func destroy_func;
String documentation;
@@ -88,8 +88,8 @@ struct NativeScriptDesc {
bool is_tool;
inline NativeScriptDesc() {
- zeromem(&create_func, sizeof(godot_instance_create_func));
- zeromem(&destroy_func, sizeof(godot_instance_destroy_func));
+ zeromem(&create_func, sizeof(godot_nativescript_instance_create_func));
+ zeromem(&destroy_func, sizeof(godot_nativescript_instance_destroy_func));
}
};
@@ -99,7 +99,7 @@ class NativeScript : public Script {
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance *> placeholders;
void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
- virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
friend class NativeScriptInstance;
@@ -125,7 +125,7 @@ protected:
public:
inline NativeScriptDesc *get_script_desc() const;
- bool inherits_script(const Ref<Script> &p_script) const;
+ bool inherits_script(const Ref<Script> &p_script) const override;
void set_class_name(String p_class_name);
String get_class_name() const;
@@ -138,48 +138,48 @@ public:
void set_script_class_icon_path(String p_icon_path);
String get_script_class_icon_path() const;
- virtual bool can_instance() const;
+ virtual bool can_instance() const override;
- virtual Ref<Script> get_base_script() const; //for script inheritance
+ virtual Ref<Script> get_base_script() const override; //for script inheritance
- virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
- virtual ScriptInstance *instance_create(Object *p_this);
- virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this);
- virtual bool instance_has(const Object *p_this) const;
+ virtual StringName get_instance_base_type() const override; // this may not work in all scripts, will return empty if so
+ virtual ScriptInstance *instance_create(Object *p_this) override;
+ virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override;
+ virtual bool instance_has(const Object *p_this) const override;
- virtual bool has_source_code() const;
- virtual String get_source_code() const;
- virtual void set_source_code(const String &p_code);
- virtual Error reload(bool p_keep_state = false);
+ virtual bool has_source_code() const override;
+ virtual String get_source_code() const override;
+ virtual void set_source_code(const String &p_code) override;
+ virtual Error reload(bool p_keep_state = false) override;
- virtual bool has_method(const StringName &p_method) const;
- virtual MethodInfo get_method_info(const StringName &p_method) const;
+ virtual bool has_method(const StringName &p_method) const override;
+ virtual MethodInfo get_method_info(const StringName &p_method) const override;
- virtual bool is_tool() const;
- virtual bool is_valid() const;
+ virtual bool is_tool() const override;
+ virtual bool is_valid() const override;
- virtual ScriptLanguage *get_language() const;
+ virtual ScriptLanguage *get_language() const override;
- virtual bool has_script_signal(const StringName &p_signal) const;
- virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+ virtual bool has_script_signal(const StringName &p_signal) const override;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override;
- virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
+ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
- virtual void update_exports(); //editor tool
- virtual void get_script_method_list(List<MethodInfo> *p_list) const;
- virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+ virtual void update_exports() override; //editor tool
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual Vector<ScriptNetData> get_rpc_methods() const override;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
+ virtual StringName get_rpc_method(uint16_t p_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual Vector<ScriptNetData> get_rset_properties() const override;
+ virtual uint16_t get_rset_property_id(const StringName &p_variable) const override;
+ virtual StringName get_rset_property(uint16_t p_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
String get_class_documentation() const;
String get_method_documentation(const StringName &p_method) const;
@@ -231,9 +231,6 @@ public:
virtual ScriptLanguage *get_language();
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
-
virtual void refcount_incremented();
virtual bool refcount_decremented();
@@ -267,7 +264,7 @@ private:
void call_libraries_cb(const StringName &name);
- Vector<Pair<bool, godot_instance_binding_functions>> binding_functions;
+ Vector<Pair<bool, godot_nativescript_instance_binding_functions>> binding_functions;
Set<Vector<void *> *> binding_instances;
Map<int, HashMap<StringName, const void *>> global_type_tags;
@@ -360,7 +357,7 @@ public:
virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max);
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max);
- int register_binding_functions(godot_instance_binding_functions p_binding_functions);
+ int register_binding_functions(godot_nativescript_instance_binding_functions p_binding_functions);
void unregister_binding_functions(int p_idx);
void *get_instance_binding_data(int p_idx, Object *p_object);
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h
index cfaf14936e..64d764029f 100644
--- a/modules/gdnative/net/multiplayer_peer_gdnative.h
+++ b/modules/gdnative/net/multiplayer_peer_gdnative.h
@@ -50,28 +50,28 @@ public:
void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl);
/* Specific to PacketPeer */
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const;
- virtual int get_available_packet_count() const;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_max_packet_size() const override;
+ virtual int get_available_packet_count() const override;
/* Specific to NetworkedMultiplayerPeer */
- virtual void set_transfer_mode(TransferMode p_mode);
- virtual TransferMode get_transfer_mode() const;
- virtual void set_target_peer(int p_peer_id);
+ virtual void set_transfer_mode(TransferMode p_mode) override;
+ virtual TransferMode get_transfer_mode() const override;
+ virtual void set_target_peer(int p_peer_id) override;
- virtual int get_packet_peer() const;
+ virtual int get_packet_peer() const override;
- virtual bool is_server() const;
+ virtual bool is_server() const override;
- virtual void poll();
+ virtual void poll() override;
- virtual int get_unique_id() const;
+ virtual int get_unique_id() const override;
- virtual void set_refuse_new_connections(bool p_enable);
- virtual bool is_refusing_new_connections() const;
+ virtual void set_refuse_new_connections(bool p_enable) override;
+ virtual bool is_refusing_new_connections() const override;
- virtual ConnectionStatus get_connection_status() const;
+ virtual ConnectionStatus get_connection_status() const override;
};
#endif // MULTIPLAYER_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h
index 78d8bb32b6..00de8f7f4c 100644
--- a/modules/gdnative/net/packet_peer_gdnative.h
+++ b/modules/gdnative/net/packet_peer_gdnative.h
@@ -50,10 +50,10 @@ public:
void set_native_packet_peer(const godot_net_packet_peer *p_impl);
/* Specific to PacketPeer */
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const;
- virtual int get_available_packet_count() const;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_max_packet_size() const override;
+ virtual int get_available_packet_count() const override;
};
#endif // PACKET_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/stream_peer_gdnative.h b/modules/gdnative/net/stream_peer_gdnative.h
index 0b2f995aa7..302fb48012 100644
--- a/modules/gdnative/net/stream_peer_gdnative.h
+++ b/modules/gdnative/net/stream_peer_gdnative.h
@@ -50,11 +50,11 @@ public:
void set_native_stream_peer(const godot_net_stream_peer *p_interface);
/* Specific to StreamPeer */
- Error put_data(const uint8_t *p_data, int p_bytes);
- Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent);
- Error get_data(uint8_t *p_buffer, int p_bytes);
- Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received);
- int get_available_bytes() const;
+ Error put_data(const uint8_t *p_data, int p_bytes) override;
+ Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
+ Error get_data(uint8_t *p_buffer, int p_bytes) override;
+ Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
+ int get_available_bytes() const override;
};
#endif // STREAM_PEER_GDNATIVE_H
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index 6309b6fde3..690d1a0432 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -62,12 +62,6 @@ public:
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- // Rely on default implementations provided by ScriptInstance for the moment.
- // Note that multilevel call could be removed in 3.0 release, so stay tuned
- // (see https://godotengine.org/qa/9244/can-override-the-_ready-and-_process-functions-child-classes)
- //virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
- //virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount);
-
virtual void notification(int p_notification);
virtual Ref<Script> get_script() const;
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
index 659289ef9b..9cd38cd4b4 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.h
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -71,7 +71,7 @@ private:
protected:
static void _bind_methods();
- bool inherits_script(const Ref<Script> &p_script) const;
+ bool inherits_script(const Ref<Script> &p_script) const override;
PluginScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, Callable::CallError &r_error);
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -79,57 +79,57 @@ protected:
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance *> placeholders;
//void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
- virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
public:
- virtual bool can_instance() const;
+ virtual bool can_instance() const override;
- virtual Ref<Script> get_base_script() const; //for script inheritance
+ virtual Ref<Script> get_base_script() const override; //for script inheritance
- virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
- virtual ScriptInstance *instance_create(Object *p_this);
- virtual bool instance_has(const Object *p_this) const;
+ virtual StringName get_instance_base_type() const override; // this may not work in all scripts, will return empty if so
+ virtual ScriptInstance *instance_create(Object *p_this) override;
+ virtual bool instance_has(const Object *p_this) const override;
- virtual bool has_source_code() const;
- virtual String get_source_code() const;
- virtual void set_source_code(const String &p_code);
- virtual Error reload(bool p_keep_state = false);
+ virtual bool has_source_code() const override;
+ virtual String get_source_code() const override;
+ virtual void set_source_code(const String &p_code) override;
+ virtual Error reload(bool p_keep_state = false) override;
// TODO: load_source_code only allow utf-8 file, should handle bytecode as well ?
virtual Error load_source_code(const String &p_path);
- virtual bool has_method(const StringName &p_method) const;
- virtual MethodInfo get_method_info(const StringName &p_method) const;
+ virtual bool has_method(const StringName &p_method) const override;
+ virtual MethodInfo get_method_info(const StringName &p_method) const override;
bool has_property(const StringName &p_method) const;
PropertyInfo get_property_info(const StringName &p_property) const;
- bool is_tool() const { return _tool; }
- bool is_valid() const { return true; }
+ bool is_tool() const override { return _tool; }
+ bool is_valid() const override { return true; }
- virtual ScriptLanguage *get_language() const;
+ virtual ScriptLanguage *get_language() const override;
- virtual bool has_script_signal(const StringName &p_signal) const;
- virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+ virtual bool has_script_signal(const StringName &p_signal) const override;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override;
- virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
+ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
- virtual void update_exports();
- virtual void get_script_method_list(List<MethodInfo> *r_methods) const;
- virtual void get_script_property_list(List<PropertyInfo> *r_properties) const;
+ virtual void update_exports() override;
+ virtual void get_script_method_list(List<MethodInfo> *r_methods) const override;
+ virtual void get_script_property_list(List<PropertyInfo> *r_properties) const override;
- virtual int get_member_line(const StringName &p_member) const;
+ virtual int get_member_line(const StringName &p_member) const override;
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual Vector<ScriptNetData> get_rpc_methods() const override;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
+ virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_property) const;
- virtual StringName get_rset_property(const uint16_t p_rset_property_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual Vector<ScriptNetData> get_rset_properties() const override;
+ virtual uint16_t get_rset_property_id(const StringName &p_property) const override;
+ virtual StringName get_rset_property(const uint16_t p_rset_property_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
PluginScript();
void init(PluginScriptLanguage *language);
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index 136af5bd1e..3a2d0b09a3 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -142,47 +142,85 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty
}
}
+ // Add symbols for staticaly linked libraries on iOS
if (p_features.has("iOS")) {
- // Register symbols in the "fake" dynamic lookup table, because dlsym does not work well on iOS.
- LibrarySymbol expected_symbols[] = {
- { "gdnative_init", true },
- { "gdnative_terminate", false },
- { "nativescript_init", false },
- { "nativescript_frame", false },
- { "nativescript_thread_enter", false },
- { "nativescript_thread_exit", false },
- { "gdnative_singleton", false }
- };
- String declare_pattern = "extern \"C\" void $name(void)$weak;\n";
- String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
- "extern void add_ios_init_callback(void (*cb)());\n";
- String linker_flags = "";
- for (unsigned long i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) {
- String full_name = lib->get_symbol_prefix() + expected_symbols[i].name;
- String code = declare_pattern.replace("$name", full_name);
- code = code.replace("$weak", expected_symbols[i].is_required ? "" : " __attribute__((weak))");
- additional_code += code;
-
- if (!expected_symbols[i].is_required) {
- if (linker_flags.length() > 0) {
- linker_flags += " ";
+ bool should_fake_dynamic = false;
+
+ List<String> entry_keys;
+ config->get_section_keys("entry", &entry_keys);
+
+ for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
+ String key = E->get();
+
+ Vector<String> tags = key.split(".");
+
+ bool skip = false;
+ for (int i = 0; i < tags.size(); i++) {
+ bool has_feature = p_features.has(tags[i]);
+
+ if (!has_feature) {
+ skip = true;
+ break;
}
- linker_flags += "-Wl,-U,_" + full_name;
}
- }
- additional_code += String("void $prefixinit() {\n").replace("$prefix", lib->get_symbol_prefix());
- String register_pattern = " if (&$name) register_dynamic_symbol((char *)\"$name\", (void *)$name);\n";
- for (unsigned long i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) {
- String full_name = lib->get_symbol_prefix() + expected_symbols[i].name;
- additional_code += register_pattern.replace("$name", full_name);
+ if (skip) {
+ continue;
+ }
+
+ String entry_lib_path = config->get_value("entry", key);
+ if (entry_lib_path.begins_with("res://") && entry_lib_path.ends_with(".a")) {
+ // If we find static library that was used for export
+ // we should add a fake lookup table.
+ // In case of dynamic library being used,
+ // this symbols will not cause any issues with library loading.
+ should_fake_dynamic = true;
+ break;
+ }
}
- additional_code += "}\n";
- additional_code += String("struct $prefixstruct {$prefixstruct() {add_ios_init_callback($prefixinit);}};\n").replace("$prefix", lib->get_symbol_prefix());
- additional_code += String("$prefixstruct $prefixstruct_instance;\n").replace("$prefix", lib->get_symbol_prefix());
- add_ios_cpp_code(additional_code);
- add_ios_linker_flags(linker_flags);
+ if (should_fake_dynamic) {
+ // Register symbols in the "fake" dynamic lookup table, because dlsym does not work well on iOS.
+ LibrarySymbol expected_symbols[] = {
+ { "gdnative_init", true },
+ { "gdnative_terminate", false },
+ { "nativescript_init", false },
+ { "nativescript_frame", false },
+ { "nativescript_thread_enter", false },
+ { "nativescript_thread_exit", false },
+ { "gdnative_singleton", false }
+ };
+ String declare_pattern = "extern \"C\" void $name(void)$weak;\n";
+ String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
+ "extern void add_ios_init_callback(void (*cb)());\n";
+ String linker_flags = "";
+ for (unsigned long i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) {
+ String full_name = lib->get_symbol_prefix() + expected_symbols[i].name;
+ String code = declare_pattern.replace("$name", full_name);
+ code = code.replace("$weak", expected_symbols[i].is_required ? "" : " __attribute__((weak))");
+ additional_code += code;
+
+ if (!expected_symbols[i].is_required) {
+ if (linker_flags.length() > 0) {
+ linker_flags += " ";
+ }
+ linker_flags += "-Wl,-U,_" + full_name;
+ }
+ }
+
+ additional_code += String("void $prefixinit() {\n").replace("$prefix", lib->get_symbol_prefix());
+ String register_pattern = " if (&$name) register_dynamic_symbol((char *)\"$name\", (void *)$name);\n";
+ for (unsigned long i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) {
+ String full_name = lib->get_symbol_prefix() + expected_symbols[i].name;
+ additional_code += register_pattern.replace("$name", full_name);
+ }
+ additional_code += "}\n";
+ additional_code += String("struct $prefixstruct {$prefixstruct() {add_ios_init_callback($prefixinit);}};\n").replace("$prefix", lib->get_symbol_prefix());
+ additional_code += String("$prefixstruct $prefixstruct_instance;\n").replace("$prefix", lib->get_symbol_prefix());
+
+ add_ios_cpp_code(additional_code);
+ add_ios_linker_flags(linker_flags);
+ }
}
}
diff --git a/modules/gdnative/tests/test_string.h b/modules/gdnative/tests/test_string.h
new file mode 100644
index 0000000000..aeb855a1c4
--- /dev/null
+++ b/modules/gdnative/tests/test_string.h
@@ -0,0 +1,1980 @@
+/*************************************************************************/
+/* test_string.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 TEST_GDNATIVE_STRING_H
+#define TEST_GDNATIVE_STRING_H
+
+namespace TestGDNativeString {
+
+#include "gdnative/string.h"
+
+#include "tests/test_macros.h"
+
+int u32scmp(const char32_t *l, const char32_t *r) {
+ for (; *l == *r && *l && *r; l++, r++)
+ ;
+ return *l - *r;
+}
+
+TEST_CASE("[GDNative String] Construct from Latin-1 char string") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Hello");
+ CHECK(u32scmp(godot_string_get_data(&s), U"Hello") == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars_and_len(&s, "Hello", 3);
+ CHECK(u32scmp(godot_string_get_data(&s), U"Hel") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct from wchar_t string") {
+ godot_string s;
+
+ godot_string_new_with_wide_chars(&s, L"Give me");
+ CHECK(u32scmp(godot_string_get_data(&s), U"Give me") == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_wide_chars_and_len(&s, L"Give me", 3);
+ CHECK(u32scmp(godot_string_get_data(&s), U"Giv") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-8 char string") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 };
+ static const uint8_t u8str[] = { 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf8_chars(&s, (const char *)u8str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 5);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ godot_char_string cs = godot_string_utf8(&s);
+ godot_string_parse_utf8(&s, godot_char_string_get_data(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char_string_destroy(&cs);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ cs = godot_string_utf8(&s);
+ godot_string_parse_utf8_with_len(&s, godot_char_string_get_data(&cs), godot_char_string_length(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char_string_destroy(&cs);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-8 string with BOM") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 };
+ static const uint8_t u8str[] = { 0xEF, 0xBB, 0xBF, 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf8_chars(&s, (const char *)u8str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 8);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-16 string") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 };
+ static const char16_t u16str[] = { 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf16_chars(&s, u16str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars_and_len(&s, u16str, 4);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ godot_char16_string cs = godot_string_utf16(&s);
+ godot_string_parse_utf16(&s, godot_char16_string_get_data(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char16_string_destroy(&cs);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ cs = godot_string_utf16(&s);
+ godot_string_parse_utf16_with_len(&s, godot_char16_string_get_data(&cs), godot_char16_string_length(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char16_string_destroy(&cs);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-16 string with BOM ") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 };
+ static const char16_t u16str[] = { 0xFEFF, 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
+ static const char16_t u16str_swap[] = { 0xFFFE, 0x4500, 0x2000, 0x3CD8, 0xA4DF, 0x0F36, 0x8830, 0x4630, 0x3CD8, 0xA4DF, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf16_chars(&s, u16str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars(&s, u16str_swap);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars_and_len(&s, u16str, 5);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars_and_len(&s, u16str_swap, 5);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct string copy") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "Hello");
+ godot_string_new_copy(&t, &s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"Hello") == 0);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct empty string") {
+ godot_string s;
+
+ godot_string_new(&s);
+ CHECK(u32scmp(godot_string_get_data(&s), U"") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] ASCII/Latin-1") {
+ godot_string s;
+ godot_string_new_with_utf32_chars(&s, U"Primero Leche");
+
+ godot_char_string cs = godot_string_ascii(&s);
+ CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0);
+ godot_char_string_destroy(&cs);
+
+ cs = godot_string_latin1(&s);
+ CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0);
+ godot_char_string_destroy(&cs);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Comparisons (equal)") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "Test Compare");
+ godot_string_new_with_latin1_chars(&t, "Test Compare");
+ CHECK(godot_string_operator_equal(&s, &t));
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] Comparisons (operator <)") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "Bees");
+
+ godot_string_new_with_latin1_chars(&t, "Elephant");
+ CHECK(godot_string_operator_less(&s, &t));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Amber");
+ CHECK(!godot_string_operator_less(&s, &t));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Beatrix");
+ CHECK(!godot_string_operator_less(&s, &t));
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Concatenation (operator +)") {
+ godot_string s, t, x;
+
+ godot_string_new_with_latin1_chars(&s, "Hel");
+ godot_string_new_with_latin1_chars(&t, "lo");
+ x = godot_string_operator_plus(&s, &t);
+ CHECK(u32scmp(godot_string_get_data(&x), U"Hello") == 0);
+ godot_string_destroy(&x);
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] Testing size and length of string") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Mellon");
+ CHECK(godot_string_length(&s) == 6);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars(&s, "Mellon1");
+ CHECK(godot_string_length(&s) == 7);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Testing for empty string") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Mellon");
+ CHECK(!godot_string_empty(&s));
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars(&s, "");
+ CHECK(godot_string_empty(&s));
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Test chr") {
+ godot_string s;
+
+ s = godot_string_chr('H');
+ CHECK(u32scmp(godot_string_get_data(&s), U"H") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_chr(0x3012);
+ CHECK(godot_string_operator_index_const(&s, 0) == 0x3012);
+ godot_string_destroy(&s);
+
+ ERR_PRINT_OFF
+ s = godot_string_chr(0xd812);
+ CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Unpaired UTF-16 surrogate
+ godot_string_destroy(&s);
+
+ s = godot_string_chr(0x20d812);
+ CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Outside UTF-32 range
+ godot_string_destroy(&s);
+ ERR_PRINT_ON
+}
+
+TEST_CASE("[GDNative String] Operator []") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Hello");
+ CHECK(*godot_string_operator_index(&s, 1) == 'e');
+ CHECK(godot_string_operator_index_const(&s, 0) == 'H');
+ CHECK(godot_string_ord_at(&s, 0) == 'H');
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Case function test") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "MoMoNgA");
+
+ t = godot_string_to_upper(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"MOMONGA") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_string_to_lower(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"momonga") == 0);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Case compare function test") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "MoMoNgA");
+ godot_string_new_with_latin1_chars(&t, "momonga");
+
+ CHECK(godot_string_casecmp_to(&s, &t) != 0);
+ CHECK(godot_string_nocasecmp_to(&s, &t) == 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] Natural compare function test") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "img2.png");
+ godot_string_new_with_latin1_chars(&t, "img10.png");
+
+ CHECK(godot_string_nocasecmp_to(&s, &t) > 0);
+ CHECK(godot_string_naturalnocasecmp_to(&s, &t) < 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] hex_encode_buffer") {
+ static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3 };
+ godot_string s = godot_string_hex_encode_buffer(u8str, 6);
+ CHECK(u32scmp(godot_string_get_data(&s), U"45e3818a8fe3") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Substr") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Killer Baby");
+ t = godot_string_substr(&s, 3, 4);
+ CHECK(u32scmp(godot_string_get_data(&t), U"ler ") == 0);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Find") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Pretty Woman Woman");
+
+ godot_string_new_with_latin1_chars(&t, "Revenge of the Monster Truck");
+ CHECK(godot_string_find(&s, &t) == -1);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "tty");
+ CHECK(godot_string_find(&s, &t) == 3);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Wo");
+ CHECK(godot_string_find_from(&s, &t, 9) == 13);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "man");
+ CHECK(godot_string_rfind(&s, &t) == 15);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Find no case") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Pretty Whale Whale");
+
+ godot_string_new_with_latin1_chars(&t, "WHA");
+ CHECK(godot_string_findn(&s, &t) == 7);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "WHA");
+ CHECK(godot_string_findn_from(&s, &t, 9) == 13);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "WHA");
+ CHECK(godot_string_rfindn(&s, &t) == 13);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Revenge of the Monster SawFish");
+ CHECK(godot_string_findn(&s, &t) == -1);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Find MK") {
+ godot_packed_string_array keys;
+ godot_packed_string_array_new(&keys);
+
+#define PUSH_KEY(x) \
+ { \
+ godot_string t; \
+ godot_string_new_with_latin1_chars(&t, x); \
+ godot_packed_string_array_push_back(&keys, &t); \
+ godot_string_destroy(&t); \
+ }
+
+ PUSH_KEY("sty")
+ PUSH_KEY("tty")
+ PUSH_KEY("man")
+
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, "Pretty Woman");
+ godot_int key = 0;
+
+ CHECK(godot_string_findmk(&s, &keys) == 3);
+ CHECK(godot_string_findmk_from_in_place(&s, &keys, 0, &key) == 3);
+ CHECK(key == 1);
+
+ CHECK(godot_string_findmk_from(&s, &keys, 5) == 9);
+ CHECK(godot_string_findmk_from_in_place(&s, &keys, 5, &key) == 9);
+ CHECK(key == 2);
+
+ godot_string_destroy(&s);
+ godot_packed_string_array_destroy(&keys);
+
+#undef PUSH_KEY
+}
+
+TEST_CASE("[GDNative String] Find and replace") {
+ godot_string s, c, w;
+ godot_string_new_with_latin1_chars(&s, "Happy Birthday, Anna!");
+ godot_string_new_with_latin1_chars(&c, "Birthday");
+ godot_string_new_with_latin1_chars(&w, "Halloween");
+ godot_string t = godot_string_replace(&s, &c, &w);
+ CHECK(u32scmp(godot_string_get_data(&t), U"Happy Halloween, Anna!") == 0);
+ godot_string_destroy(&s);
+ godot_string_destroy(&c);
+ godot_string_destroy(&w);
+
+ godot_string_new_with_latin1_chars(&c, "H");
+ godot_string_new_with_latin1_chars(&w, "W");
+ s = godot_string_replace_first(&t, &c, &w);
+ godot_string_destroy(&t);
+ godot_string_destroy(&c);
+ godot_string_destroy(&w);
+
+ CHECK(u32scmp(godot_string_get_data(&s), U"Wappy Halloween, Anna!") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Insertion") {
+ godot_string s, t, r, u;
+ godot_string_new_with_latin1_chars(&s, "Who is Frederic?");
+ godot_string_new_with_latin1_chars(&t, "?");
+ godot_string_new_with_latin1_chars(&r, " Chopin");
+
+ u = godot_string_insert(&s, godot_string_find(&s, &t), &r);
+ CHECK(u32scmp(godot_string_get_data(&u), U"Who is Frederic Chopin?") == 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+ godot_string_destroy(&r);
+ godot_string_destroy(&u);
+}
+
+TEST_CASE("[GDNative String] Number to string") {
+ godot_string s;
+ s = godot_string_num(3.141593);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_with_decimals(3.141593, 3);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3.142") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_real(3.141593);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_scientific(30000000);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3e+07") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_int64(3141593, 10);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_int64(0xA141593, 16);
+ CHECK(u32scmp(godot_string_get_data(&s), U"a141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_int64_capitalized(0xA141593, 16, true);
+ CHECK(u32scmp(godot_string_get_data(&s), U"A141593") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] String to integer") {
+ static const wchar_t *wnums[4] = { L"1237461283", L"- 22", L"0", L" - 1123412" };
+ static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" };
+ static const int num[4] = { 1237461283, -22, 0, -1123412 };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, nums[i]);
+ CHECK(godot_string_to_int(&s) == num[i]);
+ godot_string_destroy(&s);
+
+ CHECK(godot_string_char_to_int(nums[i]) == num[i]);
+ CHECK(godot_string_wchar_to_int(wnums[i]) == num[i]);
+ }
+}
+
+TEST_CASE("[GDNative String] Hex to integer") {
+ static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" };
+ static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD };
+ static const bool wo_prefix[4] = { false, true, true, true };
+ static const bool w_prefix[4] = { true, false, true, false };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, nums[i]);
+ CHECK((godot_string_hex_to_int_with_prefix(&s) == num[i]) == w_prefix[i]);
+ CHECK((godot_string_hex_to_int(&s) == num[i]) == wo_prefix[i]);
+ godot_string_destroy(&s);
+ }
+}
+
+TEST_CASE("[GDNative String] String to float") {
+ static const wchar_t *wnums[4] = { L"-12348298412.2", L"0.05", L"2.0002", L" -0.0001" };
+ static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" };
+ static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, nums[i]);
+ CHECK(!(ABS(godot_string_to_float(&s) - num[i]) > 0.00001));
+ godot_string_destroy(&s);
+
+ CHECK(!(ABS(godot_string_char_to_float(nums[i]) - num[i]) > 0.00001));
+ CHECK(!(ABS(godot_string_wchar_to_float(wnums[i], nullptr) - num[i]) > 0.00001));
+ }
+}
+
+TEST_CASE("[GDNative String] CamelCase to underscore") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "TestTestStringGD");
+
+ t = godot_string_camelcase_to_underscore(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"Test_Test_String_GD") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_string_camelcase_to_underscore_lowercased(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"test_test_string_gd") == 0);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Slicing") {
+ godot_string s, c;
+ godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus");
+ godot_string_new_with_latin1_chars(&c, ",");
+
+ const char32_t *slices[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" };
+ for (int i = 0; i < godot_string_get_slice_count(&s, &c); i++) {
+ godot_string t;
+ t = godot_string_get_slice(&s, &c, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_slicec(&s, U',', i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0);
+ godot_string_destroy(&t);
+ }
+
+ godot_string_destroy(&c);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Splitting") {
+ godot_string s, c;
+ godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus");
+ godot_string_new_with_latin1_chars(&c, ",");
+
+ godot_packed_string_array l;
+
+ const char32_t *slices_l[3] = { U"Mars", U"Jupiter", U"Saturn,Uranus" };
+ const char32_t *slices_r[3] = { U"Mars,Jupiter", U"Saturn", U"Uranus" };
+
+ l = godot_string_split_with_maxsplit(&s, &c, true, 2);
+ CHECK(godot_packed_string_array_size(&l) == 3);
+ for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
+ godot_string t = godot_packed_string_array_get(&l, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices_l[i]) == 0);
+ godot_string_destroy(&t);
+ }
+ godot_packed_string_array_destroy(&l);
+
+ l = godot_string_rsplit_with_maxsplit(&s, &c, true, 2);
+ CHECK(godot_packed_string_array_size(&l) == 3);
+ for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
+ godot_string t = godot_packed_string_array_get(&l, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices_r[i]) == 0);
+ godot_string_destroy(&t);
+ }
+ godot_packed_string_array_destroy(&l);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars(&s, "Mars Jupiter Saturn Uranus");
+ const char32_t *slices_s[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" };
+ l = godot_string_split_spaces(&s);
+ for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
+ godot_string t = godot_packed_string_array_get(&l, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices_s[i]) == 0);
+ godot_string_destroy(&t);
+ }
+ godot_packed_string_array_destroy(&l);
+ godot_string_destroy(&s);
+
+ godot_string c1, c2;
+ godot_string_new_with_latin1_chars(&c1, ";");
+ godot_string_new_with_latin1_chars(&c2, " ");
+
+ godot_string_new_with_latin1_chars(&s, "1.2;2.3 4.5");
+ const double slices_d[3] = { 1.2, 2.3, 4.5 };
+
+ godot_packed_float32_array lf = godot_string_split_floats_allow_empty(&s, &c1);
+ CHECK(godot_packed_float32_array_size(&lf) == 2);
+ for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) {
+ CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001);
+ }
+ godot_packed_float32_array_destroy(&lf);
+
+ godot_packed_string_array keys;
+ godot_packed_string_array_new(&keys);
+ godot_packed_string_array_push_back(&keys, &c1);
+ godot_packed_string_array_push_back(&keys, &c2);
+
+ lf = godot_string_split_floats_mk_allow_empty(&s, &keys);
+ CHECK(godot_packed_float32_array_size(&lf) == 3);
+ for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) {
+ CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001);
+ }
+ godot_packed_float32_array_destroy(&lf);
+
+ godot_string_destroy(&s);
+ godot_string_new_with_latin1_chars(&s, "1;2 4");
+ const int slices_i[3] = { 1, 2, 4 };
+
+ godot_packed_int32_array li = godot_string_split_ints_allow_empty(&s, &c1);
+ CHECK(godot_packed_int32_array_size(&li) == 2);
+ for (int i = 0; i < godot_packed_int32_array_size(&li); i++) {
+ CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]);
+ }
+ godot_packed_int32_array_destroy(&li);
+
+ li = godot_string_split_ints_mk_allow_empty(&s, &keys);
+ CHECK(godot_packed_int32_array_size(&li) == 3);
+ for (int i = 0; i < godot_packed_int32_array_size(&li); i++) {
+ CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]);
+ }
+ godot_packed_int32_array_destroy(&li);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&c);
+ godot_string_destroy(&c1);
+ godot_string_destroy(&c2);
+ godot_packed_string_array_destroy(&keys);
+}
+
+TEST_CASE("[GDNative String] Erasing") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Josephine is such a cute girl!");
+ godot_string_new_with_latin1_chars(&t, "cute ");
+
+ godot_string_erase(&s, godot_string_find(&s, &t), godot_string_length(&t));
+
+ CHECK(u32scmp(godot_string_get_data(&s), U"Josephine is such a girl!") == 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+struct test_27_data {
+ char const *data;
+ char const *part;
+ bool expected;
+};
+
+TEST_CASE("[GDNative String] Begins with") {
+ test_27_data tc[] = {
+ { "res://foobar", "res://", true },
+ { "res", "res://", false },
+ { "abc", "abc", true }
+ };
+ size_t count = sizeof(tc) / sizeof(tc[0]);
+ bool state = true;
+ for (size_t i = 0; state && i < count; ++i) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, tc[i].data);
+
+ state = godot_string_begins_with_char_array(&s, tc[i].part) == tc[i].expected;
+ if (state) {
+ godot_string t;
+ godot_string_new_with_latin1_chars(&t, tc[i].part);
+ state = godot_string_begins_with(&s, &t) == tc[i].expected;
+ godot_string_destroy(&t);
+ }
+ godot_string_destroy(&s);
+
+ CHECK(state);
+ if (!state) {
+ break;
+ }
+ };
+ CHECK(state);
+}
+
+TEST_CASE("[GDNative String] Ends with") {
+ test_27_data tc[] = {
+ { "res://foobar", "foobar", true },
+ { "res", "res://", false },
+ { "abc", "abc", true }
+ };
+ size_t count = sizeof(tc) / sizeof(tc[0]);
+ bool state = true;
+ for (size_t i = 0; state && i < count; ++i) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, tc[i].data);
+
+ state = godot_string_ends_with_char_array(&s, tc[i].part) == tc[i].expected;
+ if (state) {
+ godot_string t;
+ godot_string_new_with_latin1_chars(&t, tc[i].part);
+ state = godot_string_ends_with(&s, &t) == tc[i].expected;
+ godot_string_destroy(&t);
+ }
+ godot_string_destroy(&s);
+
+ CHECK(state);
+ if (!state) {
+ break;
+ }
+ };
+ CHECK(state);
+}
+
+TEST_CASE("[GDNative String] format") {
+ godot_string value_format, t;
+ godot_string_new_with_latin1_chars(&value_format, "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"");
+
+ godot_variant key_v, val_v;
+ godot_dictionary value_dictionary;
+ godot_dictionary_new(&value_dictionary);
+
+ godot_string_new_with_latin1_chars(&t, "red");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_variant_new_int(&val_v, 10);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_string_new_with_latin1_chars(&t, "green");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_variant_new_int(&val_v, 20);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_string_new_with_latin1_chars(&t, "blue");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_string_new_with_latin1_chars(&t, "bla");
+ godot_variant_new_string(&val_v, &t);
+ godot_string_destroy(&t);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_string_new_with_latin1_chars(&t, "alpha");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_variant_new_real(&val_v, 0.4);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_variant dict_v;
+ godot_variant_new_dictionary(&dict_v, &value_dictionary);
+ godot_string s = godot_string_format_with_custom_placeholder(&value_format, &dict_v, "$_");
+
+ CHECK(u32scmp(godot_string_get_data(&s), U"red=\"10\" green=\"20\" blue=\"bla\" alpha=\"0.4\"") == 0);
+
+ godot_dictionary_destroy(&value_dictionary);
+ godot_string_destroy(&s);
+ godot_variant_destroy(&dict_v);
+ godot_string_destroy(&value_format);
+}
+
+TEST_CASE("[GDNative String] sprintf") {
+ //godot_string GDAPI (const godot_string *p_self, const godot_array *p_values, godot_bool *p_error);
+ godot_string format, output;
+ godot_array args;
+ bool error;
+
+#define ARRAY_PUSH_STRING(x) \
+ { \
+ godot_variant v; \
+ godot_string t; \
+ godot_string_new_with_latin1_chars(&t, x); \
+ godot_variant_new_string(&v, &t); \
+ godot_string_destroy(&t); \
+ godot_array_push_back(&args, &v); \
+ godot_variant_destroy(&v); \
+ }
+
+#define ARRAY_PUSH_INT(x) \
+ { \
+ godot_variant v; \
+ godot_variant_new_int(&v, x); \
+ godot_array_push_back(&args, &v); \
+ godot_variant_destroy(&v); \
+ }
+
+#define ARRAY_PUSH_REAL(x) \
+ { \
+ godot_variant v; \
+ godot_variant_new_real(&v, x); \
+ godot_array_push_back(&args, &v); \
+ godot_variant_destroy(&v); \
+ }
+
+ godot_array_new(&args);
+
+ // %%
+ godot_string_new_with_latin1_chars(&format, "fish %% frog");
+ godot_array_clear(&args);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish % frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+ //////// INTS
+
+ // Int
+ godot_string_new_with_latin1_chars(&format, "fish %d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int left padded with zeroes.
+ godot_string_new_with_latin1_chars(&format, "fish %05d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 00005 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int left padded with spaces.
+ godot_string_new_with_latin1_chars(&format, "fish %5d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int right padded with spaces.
+ godot_string_new_with_latin1_chars(&format, "fish %-5d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int with sign (positive).
+ godot_string_new_with_latin1_chars(&format, "fish %+d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish +5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Negative int.
+ godot_string_new_with_latin1_chars(&format, "fish %d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(-5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish -5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Hex (lower)
+ godot_string_new_with_latin1_chars(&format, "fish %x frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(45);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 2d frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Hex (upper)
+ godot_string_new_with_latin1_chars(&format, "fish %X frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(45);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 2D frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Octal
+ godot_string_new_with_latin1_chars(&format, "fish %o frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 143 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+ ////// REALS
+
+ // Real
+ godot_string_new_with_latin1_chars(&format, "fish %f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real left-padded
+ godot_string_new_with_latin1_chars(&format, "fish %11f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real right-padded
+ godot_string_new_with_latin1_chars(&format, "fish %-11f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real given int.
+ godot_string_new_with_latin1_chars(&format, "fish %f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.000000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with sign (positive).
+ godot_string_new_with_latin1_chars(&format, "fish %+f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish +99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with 1 decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %.1f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 100.0 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with 12 decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %.12f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000000000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with no decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %.f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 100 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ /////// Strings.
+
+ // String
+ godot_string_new_with_latin1_chars(&format, "fish %s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // String left-padded
+ godot_string_new_with_latin1_chars(&format, "fish %10s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // String right-padded
+ godot_string_new_with_latin1_chars(&format, "fish %-10s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ ///// Characters
+
+ // Character as string.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("A");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Character as int.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(65);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ ///// Dynamic width
+
+ // String dynamic width
+ godot_string_new_with_latin1_chars(&format, "fish %*s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(10);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ REQUIRE(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int dynamic width
+ godot_string_new_with_latin1_chars(&format, "fish %*d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(10);
+ ARRAY_PUSH_INT(99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ REQUIRE(u32scmp(godot_string_get_data(&output), U"fish 99 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Float dynamic width
+ godot_string_new_with_latin1_chars(&format, "fish %*.*f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(10);
+ ARRAY_PUSH_INT(3);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ ///// Errors
+
+ // More formats than arguments.
+ godot_string_new_with_latin1_chars(&format, "fish %s %s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"not enough arguments for format string") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // More arguments than formats.
+ godot_string_new_with_latin1_chars(&format, "fish %s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("hello");
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"not all arguments converted during string formatting") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Incomplete format.
+ godot_string_new_with_latin1_chars(&format, "fish %10");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"incomplete format") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Bad character in format string
+ godot_string_new_with_latin1_chars(&format, "fish %&f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"unsupported format character") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Too many decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %2.2.2f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"too many decimal points in format") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // * not a number
+ godot_string_new_with_latin1_chars(&format, "fish %*f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"* wants number") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Character too long.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("sc");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Character bad type.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ godot_array t;
+ godot_array_new(&t);
+ godot_variant v;
+ godot_variant_new_array(&v, &t);
+ godot_array_destroy(&t);
+ godot_array_push_back(&args, &v);
+ godot_variant_destroy(&v);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ godot_array_destroy(&args);
+#undef ARRAY_PUSH_INT
+#undef ARRAY_PUSH_REAL
+#undef ARRAY_PUSH_STRING
+}
+
+TEST_CASE("[GDNative String] is_numeric") {
+#define IS_NUM_TEST(x, r) \
+ { \
+ godot_string t; \
+ godot_string_new_with_latin1_chars(&t, x); \
+ CHECK(godot_string_is_numeric(&t) == r); \
+ godot_string_destroy(&t); \
+ }
+
+ IS_NUM_TEST("12", true);
+ IS_NUM_TEST("1.2", true);
+ IS_NUM_TEST("AF", false);
+ IS_NUM_TEST("-12", true);
+ IS_NUM_TEST("-1.2", true);
+
+#undef IS_NUM_TEST
+}
+
+TEST_CASE("[GDNative String] pad") {
+ godot_string s, c;
+ godot_string_new_with_latin1_chars(&s, "test");
+ godot_string_new_with_latin1_chars(&c, "x");
+
+ godot_string l = godot_string_lpad_with_custom_character(&s, 10, &c);
+ CHECK(u32scmp(godot_string_get_data(&l), U"xxxxxxtest") == 0);
+ godot_string_destroy(&l);
+
+ godot_string r = godot_string_rpad_with_custom_character(&s, 10, &c);
+ CHECK(u32scmp(godot_string_get_data(&r), U"testxxxxxx") == 0);
+ godot_string_destroy(&r);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&c);
+
+ godot_string_new_with_latin1_chars(&s, "10.10");
+ c = godot_string_pad_decimals(&s, 4);
+ CHECK(u32scmp(godot_string_get_data(&c), U"10.1000") == 0);
+ godot_string_destroy(&c);
+ c = godot_string_pad_zeros(&s, 4);
+ CHECK(u32scmp(godot_string_get_data(&c), U"0010.10") == 0);
+ godot_string_destroy(&c);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] is_subsequence_of") {
+ godot_string a, t;
+ godot_string_new_with_latin1_chars(&a, "is subsequence of");
+
+ godot_string_new_with_latin1_chars(&t, "sub");
+ CHECK(godot_string_is_subsequence_of(&t, &a));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Sub");
+ CHECK(!godot_string_is_subsequence_of(&t, &a));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Sub");
+ CHECK(godot_string_is_subsequence_ofi(&t, &a));
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&a);
+}
+
+TEST_CASE("[GDNative String] match") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "*.png");
+
+ godot_string_new_with_latin1_chars(&t, "img1.png");
+ CHECK(godot_string_match(&t, &s));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "img1.jpeg");
+ CHECK(!godot_string_match(&t, &s));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "img1.Png");
+ CHECK(!godot_string_match(&t, &s));
+ CHECK(godot_string_matchn(&t, &s));
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] IPVX address to string") {
+ godot_string ip;
+
+ godot_string_new_with_latin1_chars(&ip, "192.168.0.1");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "192.368.0.1");
+ CHECK(!godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8:85j3:0000:0000:8a2e:0370:7334");
+ CHECK(!godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8:85f345:0000:0000:8a2e:0370:7334");
+ CHECK(!godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8::0:8a2e:370:7334");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "::ffff:192.168.0.1");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+}
+
+TEST_CASE("[GDNative String] Capitalize against many strings") {
+#define CAP_TEST(i, o) \
+ godot_string_new_with_latin1_chars(&input, i); \
+ godot_string_new_with_latin1_chars(&output, o); \
+ test = godot_string_capitalize(&input); \
+ CHECK(u32scmp(godot_string_get_data(&output), godot_string_get_data(&test)) == 0); \
+ godot_string_destroy(&input); \
+ godot_string_destroy(&output); \
+ godot_string_destroy(&test);
+
+ godot_string input, output, test;
+
+ CAP_TEST("bytes2var", "Bytes 2 Var");
+ CAP_TEST("linear2db", "Linear 2 Db");
+ CAP_TEST("vector3", "Vector 3");
+ CAP_TEST("sha256", "Sha 256");
+ CAP_TEST("2db", "2 Db");
+ CAP_TEST("PascalCase", "Pascal Case");
+ CAP_TEST("PascalPascalCase", "Pascal Pascal Case");
+ CAP_TEST("snake_case", "Snake Case");
+ CAP_TEST("snake_snake_case", "Snake Snake Case");
+ CAP_TEST("sha256sum", "Sha 256 Sum");
+ CAP_TEST("cat2dog", "Cat 2 Dog");
+ CAP_TEST("function(name)", "Function(name)");
+ CAP_TEST("snake_case_function(snake_case_arg)", "Snake Case Function(snake Case Arg)");
+ CAP_TEST("snake_case_function( snake_case_arg )", "Snake Case Function( Snake Case Arg )");
+
+#undef CAP_TEST
+}
+
+TEST_CASE("[GDNative String] lstrip and rstrip") {
+#define LSTRIP_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_latin1_chars(&xx, x); \
+ godot_string_new_with_latin1_chars(&yy, y); \
+ godot_string_new_with_latin1_chars(&zz, z); \
+ rr = godot_string_lstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+#define RSTRIP_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_latin1_chars(&xx, x); \
+ godot_string_new_with_latin1_chars(&yy, y); \
+ godot_string_new_with_latin1_chars(&zz, z); \
+ rr = godot_string_rstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+#define LSTRIP_UTF8_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_utf8_chars(&xx, x); \
+ godot_string_new_with_utf8_chars(&yy, y); \
+ godot_string_new_with_utf8_chars(&zz, z); \
+ rr = godot_string_lstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+#define RSTRIP_UTF8_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_utf8_chars(&xx, x); \
+ godot_string_new_with_utf8_chars(&yy, y); \
+ godot_string_new_with_utf8_chars(&zz, z); \
+ rr = godot_string_rstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+ bool state = true;
+
+ // strip none
+ LSTRIP_TEST("abc", "", "abc");
+ RSTRIP_TEST("abc", "", "abc");
+ // strip one
+ LSTRIP_TEST("abc", "a", "bc");
+ RSTRIP_TEST("abc", "c", "ab");
+ // strip lots
+ LSTRIP_TEST("bababbababccc", "ab", "ccc");
+ RSTRIP_TEST("aaabcbcbcbbcbbc", "cb", "aaa");
+ // strip empty string
+ LSTRIP_TEST("", "", "");
+ RSTRIP_TEST("", "", "");
+ // strip to empty string
+ LSTRIP_TEST("abcabcabc", "bca", "");
+ RSTRIP_TEST("abcabcabc", "bca", "");
+ // don't strip wrong end
+ LSTRIP_TEST("abc", "c", "abc");
+ LSTRIP_TEST("abca", "a", "bca");
+ RSTRIP_TEST("abc", "a", "abc");
+ RSTRIP_TEST("abca", "a", "abc");
+ // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
+ // and the same second as "ÿ" (\u00ff)
+ LSTRIP_UTF8_TEST("¿", "µÿ", "¿");
+ RSTRIP_UTF8_TEST("¿", "µÿ", "¿");
+ LSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "¿ÿ");
+ RSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "µ¿");
+
+ // the above tests repeated with additional superfluous strip chars
+
+ // strip none
+ LSTRIP_TEST("abc", "qwjkl", "abc");
+ RSTRIP_TEST("abc", "qwjkl", "abc");
+ // strip one
+ LSTRIP_TEST("abc", "qwajkl", "bc");
+ RSTRIP_TEST("abc", "qwcjkl", "ab");
+ // strip lots
+ LSTRIP_TEST("bababbababccc", "qwabjkl", "ccc");
+ RSTRIP_TEST("aaabcbcbcbbcbbc", "qwcbjkl", "aaa");
+ // strip empty string
+ LSTRIP_TEST("", "qwjkl", "");
+ RSTRIP_TEST("", "qwjkl", "");
+ // strip to empty string
+ LSTRIP_TEST("abcabcabc", "qwbcajkl", "");
+ RSTRIP_TEST("abcabcabc", "qwbcajkl", "");
+ // don't strip wrong end
+ LSTRIP_TEST("abc", "qwcjkl", "abc");
+ LSTRIP_TEST("abca", "qwajkl", "bca");
+ RSTRIP_TEST("abc", "qwajkl", "abc");
+ RSTRIP_TEST("abca", "qwajkl", "abc");
+ // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
+ // and the same second as "ÿ" (\u00ff)
+ LSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿");
+ RSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿");
+ LSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "¿ÿ");
+ RSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "µ¿");
+
+ CHECK(state);
+
+#undef LSTRIP_TEST
+#undef RSTRIP_TEST
+#undef LSTRIP_UTF8_TEST
+#undef RSTRIP_UTF8_TEST
+}
+
+TEST_CASE("[GDNative String] Cyrillic to_lower()") {
+ godot_string upper, lower, test;
+ godot_string_new_with_utf8_chars(&upper, "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ");
+ godot_string_new_with_utf8_chars(&lower, "абвгдеёжзийклмнопрстуфхцчшщъыьэюя");
+
+ test = godot_string_to_lower(&upper);
+
+ CHECK((u32scmp(godot_string_get_data(&test), godot_string_get_data(&lower)) == 0));
+
+ godot_string_destroy(&upper);
+ godot_string_destroy(&lower);
+ godot_string_destroy(&test);
+}
+
+TEST_CASE("[GDNative String] Count and countn functionality") {
+#define COUNT_TEST(x, y, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_count(&s, &t, 0, 0) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+
+#define COUNTR_TEST(x, y, a, b, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_count(&s, &t, a, b) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+
+#define COUNTN_TEST(x, y, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_countn(&s, &t, 0, 0) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+
+#define COUNTNR_TEST(x, y, a, b, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_countn(&s, &t, a, b) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+ bool state = true;
+
+ COUNT_TEST("", "Test", 0);
+ COUNT_TEST("Test", "", 0);
+ COUNT_TEST("Test", "test", 0);
+ COUNT_TEST("Test", "TEST", 0);
+ COUNT_TEST("TEST", "TEST", 1);
+ COUNT_TEST("Test", "Test", 1);
+ COUNT_TEST("aTest", "Test", 1);
+ COUNT_TEST("Testa", "Test", 1);
+ COUNT_TEST("TestTestTest", "Test", 3);
+ COUNT_TEST("TestTestTest", "TestTest", 1);
+ COUNT_TEST("TestGodotTestGodotTestGodot", "Test", 3);
+
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 8, 1);
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 12, 2);
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 16, 3);
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 0, 3);
+
+ COUNTN_TEST("Test", "test", 1);
+ COUNTN_TEST("Test", "TEST", 1);
+ COUNTN_TEST("testTest-Testatest", "tEst", 4);
+ COUNTNR_TEST("testTest-TeStatest", "tEsT", 4, 16, 2);
+
+ CHECK(state);
+
+#undef COUNT_TEST
+#undef COUNTR_TEST
+#undef COUNTN_TEST
+#undef COUNTNR_TEST
+}
+
+TEST_CASE("[GDNative String] Bigrams") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "abcd");
+ godot_packed_string_array bigr = godot_string_bigrams(&s);
+ godot_string_destroy(&s);
+
+ CHECK(godot_packed_string_array_size(&bigr) == 3);
+
+ t = godot_packed_string_array_get(&bigr, 0);
+ CHECK(u32scmp(godot_string_get_data(&t), U"ab") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_packed_string_array_get(&bigr, 1);
+ CHECK(u32scmp(godot_string_get_data(&t), U"bc") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_packed_string_array_get(&bigr, 2);
+ CHECK(u32scmp(godot_string_get_data(&t), U"cd") == 0);
+ godot_string_destroy(&t);
+
+ godot_packed_string_array_destroy(&bigr);
+}
+
+TEST_CASE("[GDNative String] c-escape/unescape") {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\"");
+ godot_string t = godot_string_c_escape(&s);
+ godot_string u = godot_string_c_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] dedent") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, " aaa\n bbb");
+ godot_string_new_with_latin1_chars(&t, "aaa\nbbb");
+ godot_string u = godot_string_dedent(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Path functions") {
+ static const char *path[4] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc" };
+ static const char *base_dir[4] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot" };
+ static const char *base_name[4] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test" };
+ static const char *ext[4] = { "tscn", "xscn", "scn", "doc" };
+ static const char *file[4] = { "test.tscn", "test.xscn", "test.scn", "test.doc" };
+ static const bool abs[4] = { true, true, false, false };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s, t, u, f;
+ godot_string_new_with_latin1_chars(&s, path[i]);
+
+ t = godot_string_get_base_dir(&s);
+ godot_string_new_with_latin1_chars(&u, base_dir[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_basename(&s);
+ godot_string_new_with_latin1_chars(&u, base_name[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_extension(&s);
+ godot_string_new_with_latin1_chars(&u, ext[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_file(&s);
+ godot_string_new_with_latin1_chars(&u, file[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string s_simp;
+ s_simp = godot_string_simplify_path(&s);
+ t = godot_string_get_base_dir(&s_simp);
+ godot_string_new_with_latin1_chars(&u, file[i]);
+ f = godot_string_plus_file(&t, &u);
+ CHECK(u32scmp(godot_string_get_data(&f), godot_string_get_data(&s_simp)) == 0);
+ godot_string_destroy(&f);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s_simp);
+
+ CHECK(godot_string_is_abs_path(&s) == abs[i]);
+ CHECK(godot_string_is_rel_path(&s) != abs[i]);
+
+ godot_string_destroy(&s);
+ }
+
+ static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" };
+ static const bool valid[3] = { true, false, false };
+ for (int i = 0; i < 3; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, file_name[i]);
+ CHECK(godot_string_is_valid_filename(&s) == valid[i]);
+ godot_string_destroy(&s);
+ }
+}
+
+TEST_CASE("[GDNative String] hash") {
+ godot_string a, b, c;
+ godot_string_new_with_latin1_chars(&a, "Test");
+ godot_string_new_with_latin1_chars(&b, "Test");
+ godot_string_new_with_latin1_chars(&c, "West");
+ CHECK(godot_string_hash(&a) == godot_string_hash(&b));
+ CHECK(godot_string_hash(&a) != godot_string_hash(&c));
+
+ CHECK(godot_string_hash64(&a) == godot_string_hash64(&b));
+ CHECK(godot_string_hash64(&a) != godot_string_hash64(&c));
+
+ godot_string_destroy(&a);
+ godot_string_destroy(&b);
+ godot_string_destroy(&c);
+}
+
+TEST_CASE("[GDNative String] http_escape/unescape") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'");
+ godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3A%27docs%27");
+
+ u = godot_string_http_escape(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+
+ u = godot_string_http_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] percent_encode/decode") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'");
+ godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3a%27docs%27");
+
+ u = godot_string_percent_encode(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+
+ u = godot_string_percent_decode(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] xml_escape/unescape") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "\"Test\" <test@test&'test'>");
+
+ t = godot_string_xml_escape_with_quotes(&s);
+ u = godot_string_xml_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_xml_escape(&s);
+ u = godot_string_xml_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Strip escapes") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "\t\tTest Test\r\n Test");
+ godot_string_new_with_latin1_chars(&t, "Test Test Test");
+
+ u = godot_string_strip_escapes(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Strip edges") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "\t Test Test ");
+
+ godot_string_new_with_latin1_chars(&t, "Test Test ");
+ u = godot_string_strip_edges(&s, true, false);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "\t Test Test");
+ u = godot_string_strip_edges(&s, false, true);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Test Test");
+ u = godot_string_strip_edges(&s, true, true);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Similarity") {
+ godot_string a, b, c;
+ godot_string_new_with_latin1_chars(&a, "Test");
+ godot_string_new_with_latin1_chars(&b, "West");
+ godot_string_new_with_latin1_chars(&c, "Toad");
+
+ CHECK(godot_string_similarity(&a, &b) > godot_string_similarity(&a, &c));
+
+ godot_string_destroy(&a);
+ godot_string_destroy(&b);
+ godot_string_destroy(&c);
+}
+
+TEST_CASE("[GDNative String] Trim") {
+ godot_string s, t, u, p;
+ godot_string_new_with_latin1_chars(&s, "aaaTestbbb");
+
+ godot_string_new_with_latin1_chars(&p, "aaa");
+ godot_string_new_with_latin1_chars(&t, "Testbbb");
+ u = godot_string_trim_prefix(&s, &p);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&p);
+
+ godot_string_new_with_latin1_chars(&p, "bbb");
+ godot_string_new_with_latin1_chars(&t, "aaaTest");
+ u = godot_string_trim_suffix(&s, &p);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&p);
+
+ godot_string_new_with_latin1_chars(&p, "Test");
+ u = godot_string_trim_suffix(&s, &p);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&p);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Right/Left") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "aaaTestbbb");
+ // ^
+
+ godot_string_new_with_latin1_chars(&t, "tbbb");
+ u = godot_string_right(&s, 6);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "aaaTes");
+ u = godot_string_left(&s, 6);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Repeat") {
+ godot_string t, u;
+ godot_string_new_with_latin1_chars(&t, "ab");
+
+ u = godot_string_repeat(&t, 4);
+ CHECK(u32scmp(godot_string_get_data(&u), U"abababab") == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] SHA1/SHA256/MD5") {
+ godot_string s, t, sha1, sha256, md5;
+ godot_string_new_with_latin1_chars(&s, "Godot");
+ godot_string_new_with_latin1_chars(&sha1, "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201");
+ static uint8_t sha1_buf[20] = {
+ 0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3,
+ 0x9D, 0xC2, 0xD2, 0x01
+ };
+ godot_string_new_with_latin1_chars(&sha256, "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8");
+ static uint8_t sha256_buf[32] = {
+ 0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF,
+ 0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8
+ };
+ godot_string_new_with_latin1_chars(&md5, "4a336d087aeb0390da10ee2ea7cb87f8");
+ static uint8_t md5_buf[16] = {
+ 0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8
+ };
+
+ godot_packed_byte_array buf = godot_string_sha1_buffer(&s);
+ CHECK(memcmp(sha1_buf, godot_packed_byte_array_ptr(&buf), 20) == 0);
+ godot_packed_byte_array_destroy(&buf);
+
+ t = godot_string_sha1_text(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha1)) == 0);
+ godot_string_destroy(&t);
+
+ buf = godot_string_sha256_buffer(&s);
+ CHECK(memcmp(sha256_buf, godot_packed_byte_array_ptr(&buf), 32) == 0);
+ godot_packed_byte_array_destroy(&buf);
+
+ t = godot_string_sha256_text(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha256)) == 0);
+ godot_string_destroy(&t);
+
+ buf = godot_string_md5_buffer(&s);
+ CHECK(memcmp(md5_buf, godot_packed_byte_array_ptr(&buf), 16) == 0);
+ godot_packed_byte_array_destroy(&buf);
+
+ t = godot_string_md5_text(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&md5)) == 0);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&sha1);
+ godot_string_destroy(&sha256);
+ godot_string_destroy(&md5);
+}
+
+TEST_CASE("[GDNative String] Join") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, ", ");
+
+ godot_packed_string_array parts;
+ godot_packed_string_array_new(&parts);
+ godot_string_new_with_latin1_chars(&t, "One");
+ godot_packed_string_array_push_back(&parts, &t);
+ godot_string_destroy(&t);
+ godot_string_new_with_latin1_chars(&t, "B");
+ godot_packed_string_array_push_back(&parts, &t);
+ godot_string_destroy(&t);
+ godot_string_new_with_latin1_chars(&t, "C");
+ godot_packed_string_array_push_back(&parts, &t);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&u, "One, B, C");
+ t = godot_string_join(&s, &parts);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+ godot_packed_string_array_destroy(&parts);
+}
+
+TEST_CASE("[GDNative String] Is_*") {
+ static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" };
+ static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false };
+ static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false };
+ static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false };
+ static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false };
+ static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false };
+ static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false };
+
+ for (int i = 0; i < 12; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, data[i]);
+ CHECK(godot_string_is_numeric(&s) == isnum[i]);
+ CHECK(godot_string_is_valid_integer(&s) == isint[i]);
+ CHECK(godot_string_is_valid_hex_number(&s, false) == ishex[i]);
+ CHECK(godot_string_is_valid_hex_number(&s, true) == ishex_p[i]);
+ CHECK(godot_string_is_valid_float(&s) == isflt[i]);
+ CHECK(godot_string_is_valid_identifier(&s) == isid[i]);
+ godot_string_destroy(&s);
+ }
+}
+
+TEST_CASE("[GDNative String] humanize_size") {
+ godot_string s;
+
+ s = godot_string_humanize_size(1000);
+ CHECK(u32scmp(godot_string_get_data(&s), U"1000 B") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(1025);
+ CHECK(u32scmp(godot_string_get_data(&s), U"1.00 KiB") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(1025300);
+ CHECK(u32scmp(godot_string_get_data(&s), U"1001.2 KiB") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(100523550);
+ CHECK(u32scmp(godot_string_get_data(&s), U"95.86 MiB") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(5345555000);
+ CHECK(u32scmp(godot_string_get_data(&s), U"4.97 GiB") == 0);
+ godot_string_destroy(&s);
+}
+
+} // namespace TestGDNativeString
+
+#endif // TEST_GDNATIVE_STRING_H
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
index 9d9c5b6473..fe7c10cad9 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
@@ -214,6 +214,11 @@ void VideoStreamPlaybackGDNative::cleanup() {
if (pcm) {
memfree(pcm);
}
+ if (file) {
+ file->close();
+ memdelete(file);
+ file = nullptr;
+ }
pcm = nullptr;
time = 0;
num_channels = -1;
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h
index 53017a6a97..408d4a2454 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.h
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.h
@@ -146,32 +146,32 @@ public:
bool open_file(const String &p_file);
- virtual void stop();
- virtual void play();
+ virtual void stop() override;
+ virtual void play() override;
- virtual bool is_playing() const;
+ virtual bool is_playing() const override;
- virtual void set_paused(bool p_paused);
- virtual bool is_paused() const;
+ virtual void set_paused(bool p_paused) override;
+ virtual bool is_paused() const override;
- virtual void set_loop(bool p_enable);
- virtual bool has_loop() const;
+ virtual void set_loop(bool p_enable) override;
+ virtual bool has_loop() const override;
- virtual float get_length() const;
+ virtual float get_length() const override;
- virtual float get_playback_position() const;
- virtual void seek(float p_time);
+ virtual float get_playback_position() const override;
+ virtual void seek(float p_time) override;
- virtual void set_audio_track(int p_idx);
+ virtual void set_audio_track(int p_idx) override;
//virtual int mix(int16_t* p_buffer,int p_frames)=0;
- virtual Ref<Texture2D> get_texture() const;
- virtual void update(float p_delta);
+ virtual Ref<Texture2D> get_texture() const override;
+ virtual void update(float p_delta) override;
- virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata);
- virtual int get_channels() const;
- virtual int get_mix_rate() const;
+ virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override;
+ virtual int get_channels() const override;
+ virtual int get_mix_rate() const override;
};
class VideoStreamGDNative : public VideoStream {
@@ -188,8 +188,8 @@ public:
void set_file(const String &p_file);
String get_file();
- virtual void set_audio_track(int p_track);
- virtual Ref<VideoStreamPlayback> instance_playback();
+ virtual void set_audio_track(int p_track) override;
+ virtual Ref<VideoStreamPlayback> instance_playback() override;
VideoStreamGDNative() {}
};
diff --git a/modules/gdnative/xr/xr_interface_gdnative.h b/modules/gdnative/xr/xr_interface_gdnative.h
index 64f1282a7e..de96487397 100644
--- a/modules/gdnative/xr/xr_interface_gdnative.h
+++ b/modules/gdnative/xr/xr_interface_gdnative.h
@@ -58,34 +58,34 @@ public:
void set_interface(const godot_xr_interface_gdnative *p_interface);
- virtual StringName get_name() const;
- virtual int get_capabilities() const;
+ virtual StringName get_name() const override;
+ virtual int get_capabilities() const override;
- virtual bool is_initialized() const;
- virtual bool initialize();
- virtual void uninitialize();
+ virtual bool is_initialized() const override;
+ virtual bool initialize() override;
+ virtual void uninitialize() override;
/** specific to AR **/
- virtual bool get_anchor_detection_is_enabled() const;
- virtual void set_anchor_detection_is_enabled(bool p_enable);
- virtual int get_camera_feed_id();
+ virtual bool get_anchor_detection_is_enabled() const override;
+ virtual void set_anchor_detection_is_enabled(bool p_enable) override;
+ virtual int get_camera_feed_id() override;
/** rendering and internal **/
- virtual Size2 get_render_targetsize();
- virtual bool is_stereo();
- virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform);
+ virtual Size2 get_render_targetsize() override;
+ virtual bool is_stereo() override;
+ virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override;
// we expose a Vector<float> version of this function to GDNative
Vector<float> _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far);
// and a CameraMatrix version to XRServer
- virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far);
+ virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
- virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye);
- virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect);
+ virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override;
+ virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
- virtual void process();
- virtual void notification(int p_what);
+ virtual void process() override;
+ virtual void notification(int p_what) override;
};
#endif // XR_INTERFACE_GDNATIVE_H
diff --git a/modules/gdnavigation/nav_map.cpp b/modules/gdnavigation/nav_map.cpp
index c7df6dc1fb..7919e6a01f 100644
--- a/modules/gdnavigation/nav_map.cpp
+++ b/modules/gdnavigation/nav_map.cpp
@@ -155,7 +155,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
least_cost_poly->poly->points[(i + 1) % least_cost_poly->poly->points.size()].pos
};
- const Vector3 new_entry = Geometry::get_closest_point_to_segment(least_cost_poly->entry, edge_line);
+ const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, edge_line);
const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance;
#else
const float new_distance = least_cost_poly->poly->center.distance_to(edge.other_polygon->center) + least_cost_poly->traveled_distance;
@@ -299,7 +299,6 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
left = p->poly->points[prev].pos;
right = p->poly->points[prev_n].pos;
- //if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5).dot(up) < 0){
if (p->poly->clockwise) {
SWAP(left, right);
}
@@ -414,7 +413,7 @@ Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector
for (size_t point_id = 0; point_id < p.points.size(); point_id += 1) {
Vector3 a, b;
- Geometry::get_closest_points_between_segments(
+ Geometry3D::get_closest_points_between_segments(
p_from,
p_to,
p.points[point_id].pos,
diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
index 5fe1060aae..648f4f7cdd 100644
--- a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
+++ b/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
@@ -95,13 +95,15 @@ void NavigationMeshEditor::_bind_methods() {
NavigationMeshEditor::NavigationMeshEditor() {
bake_hbox = memnew(HBoxContainer);
- button_bake = memnew(ToolButton);
+ button_bake = memnew(Button);
+ button_bake->set_flat(true);
bake_hbox->add_child(button_bake);
button_bake->set_toggle_mode(true);
button_bake->set_text(TTR("Bake NavMesh"));
button_bake->connect("pressed", callable_mp(this, &NavigationMeshEditor::_bake_pressed));
- button_reset = memnew(ToolButton);
+ button_reset = memnew(Button);
+ button_reset->set_flat(true);
bake_hbox->add_child(button_reset);
// No button text, we only use a revert icon which is set when entering the tree.
button_reset->set_tooltip(TTR("Clear the navigation mesh."));
diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.h b/modules/gdnavigation/navigation_mesh_editor_plugin.h
index da3a981f8c..f09182fff4 100644
--- a/modules/gdnavigation/navigation_mesh_editor_plugin.h
+++ b/modules/gdnavigation/navigation_mesh_editor_plugin.h
@@ -46,8 +46,8 @@ class NavigationMeshEditor : public Control {
AcceptDialog *err_dialog;
HBoxContainer *bake_hbox;
- ToolButton *button_bake;
- ToolButton *button_reset;
+ Button *button_bake;
+ Button *button_reset;
Label *bake_info;
NavigationRegion3D *node;
@@ -73,11 +73,11 @@ class NavigationMeshEditorPlugin : public EditorPlugin {
EditorNode *editor;
public:
- virtual String get_name() const { return "NavigationMesh"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_object);
- virtual bool handles(Object *p_object) const;
- virtual void make_visible(bool p_visible);
+ virtual String get_name() const override { return "NavigationMesh"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
NavigationMeshEditorPlugin(EditorNode *p_node);
~NavigationMeshEditorPlugin();
diff --git a/modules/gdnavigation/navigation_mesh_generator.cpp b/modules/gdnavigation/navigation_mesh_generator.cpp
index 7dc08fbf29..5329600e39 100644
--- a/modules/gdnavigation/navigation_mesh_generator.cpp
+++ b/modules/gdnavigation/navigation_mesh_generator.cpp
@@ -218,7 +218,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s);
if (convex_polygon) {
Vector<Vector3> varr = Variant(convex_polygon->get_points());
- Geometry::MeshData md;
+ Geometry3D::MeshData md;
Error err = QuickHull::build(varr, md);
@@ -226,7 +226,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
PackedVector3Array faces;
for (int j = 0; j < md.faces.size(); ++j) {
- Geometry::MeshData::Face face = md.faces[j];
+ Geometry3D::MeshData::Face face = md.faces[j];
for (int k = 2; k < face.indices.size(); ++k) {
faces.push_back(md.vertices[face.indices[0]]);
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index e58a1d8edc..5c8cbdf869 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -17,3 +17,7 @@ if env["tools"]:
# Using a define in the disabled case, to avoid having an extra define
# in regular builds where all modules are enabled.
env_gdscript.Append(CPPDEFINES=["GDSCRIPT_NO_LSP"])
+
+if env["tests"]:
+ env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"])
+ env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp")
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index be159b6407..613039754f 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -52,7 +52,7 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Returns the absolute value of parameter [code]s[/code] (i.e. unsigned value, works for integer and float).
+ Returns the absolute value of parameter [code]s[/code] (i.e. positive value).
[codeblock]
# a is 1
a = abs(-1)
@@ -112,7 +112,7 @@
</argument>
<description>
Returns the arc tangent of [code]s[/code] in radians. Use it to get the angle from an angle's tangent in trigonometry: [code]atan(tan(angle)) == angle[/code].
- The method cannot know in which quadrant the angle should fall. See [method atan2] if you always want an exact angle.
+ The method cannot know in which quadrant the angle should fall. See [method atan2] if you have both [code]y[/code] and [code]x[/code].
[codeblock]
a = atan(0.5) # a is 0.463648
[/codeblock]
@@ -127,6 +127,7 @@
</argument>
<description>
Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. To compute the value, the method takes into account the sign of both arguments in order to determine the quadrant.
+ Important note: The Y coordinate comes first, by convention.
[codeblock]
a = atan2(0, -1) # a is 3.141593
[/codeblock]
@@ -161,11 +162,12 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Rounds [code]s[/code] upward, returning the smallest integral value that is not less than [code]s[/code].
+ Rounds [code]s[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]s[/code].
[codeblock]
i = ceil(1.45) # i is 2
i = ceil(1.001) # i is 2
[/codeblock]
+ See also [method floor], [method round], and [method stepify].
</description>
</method>
<method name="char">
@@ -283,7 +285,7 @@
<argument index="0" name="deg" type="float">
</argument>
<description>
- Returns degrees converted to radians.
+ Converts an angle expressed in degrees to radians.
[codeblock]
# r is 3.141593
r = deg2rad(180)
@@ -307,7 +309,7 @@
<argument index="1" name="curve" type="float">
</argument>
<description>
- Easing function, based on exponent. 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in.
+ Easing function, based on exponent. The curve values are: 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in.
</description>
</method>
<method name="exp">
@@ -317,7 +319,7 @@
</argument>
<description>
The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it.
- [b]e[/b] has an approximate value of 2.71828.
+ [b]e[/b] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code].
For exponents to other bases use the method [method pow].
[codeblock]
a = exp(2) # Approximately 7.39
@@ -330,13 +332,14 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Rounds [code]s[/code] to the closest smaller integer and returns it.
+ Rounds [code]s[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]s[/code].
[codeblock]
# a is 2.0
a = floor(2.99)
# a is -3.0
a = floor(-2.99)
[/codeblock]
+ See also [method ceil], [method round], and [method stepify].
[b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(s)[/code] directly.
</description>
</method>
@@ -366,24 +369,19 @@
<description>
Returns the floating-point modulus of [code]a/b[/code] that wraps equally in positive and negative.
[codeblock]
- var i = -6
- while i &lt; 5:
- prints(i, fposmod(i, 3))
- i += 1
+ for i in 7:
+ var x = 0.5 * i - 1.5
+ print("%4.1f %4.1f %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)])
[/codeblock]
Produces:
[codeblock]
- -6 0
- -5 1
- -4 2
- -3 0
- -2 1
- -1 2
- 0 0
- 1 1
- 2 2
- 3 0
- 4 1
+ -1.5 -0.0 0.0
+ -1.0 -1.0 0.5
+ -0.5 -0.5 1.0
+ 0.0 0.0 0.0
+ 0.5 0.5 0.5
+ 1.0 1.0 1.0
+ 1.5 0.0 0.0
[/codeblock]
</description>
</method>
@@ -504,6 +502,8 @@
</argument>
<description>
Returns [code]true[/code] if [code]a[/code] and [code]b[/code] are approximately equal to each other.
+ Here, approximately equal means that [code]a[/code] and [code]b[/code] are within a small internal epsilon of each other, which scales with the magnitude of the numbers.
+ Infinity values of the same sign are considered equal.
</description>
</method>
<method name="is_inf">
@@ -530,7 +530,7 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Returns whether [code]s[/code] is a NaN (Not-A-Number) value.
+ Returns whether [code]s[/code] is a NaN ("Not a Number" or invalid) value.
</description>
</method>
<method name="is_zero_approx">
@@ -540,6 +540,7 @@
</argument>
<description>
Returns [code]true[/code] if [code]s[/code] is zero or almost zero.
+ This method is faster than using [method is_equal_approx] with one value as zero.
</description>
</method>
<method name="len">
@@ -619,11 +620,11 @@
<argument index="0" name="path" type="String">
</argument>
<description>
- Loads a resource from the filesystem located at [code]path[/code].
- [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing [b]Copy Path[/b].
+ Loads a resource from the filesystem located at [code]path[/code]. The resource is loaded on the method call (unless it's referenced already elsewhere, e.g. in another script or in the scene), which might cause slight delay, especially when loading scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload].
+ [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
[codeblock]
- # Load a scene called main located in the root of the project directory.
- var main = load("res://main.tscn")
+ # Load a scene called main located in the root of the project directory and cache it in a variable.
+ var main = load("res://main.tscn") # main will contain a PackedScene resource.
[/codeblock]
[b]Important:[/b] The path must be absolute, a local path will just return [code]null[/code].
</description>
@@ -639,6 +640,7 @@
[codeblock]
log(10) # Returns 2.302585
[/codeblock]
+ [b]Note:[/b] The logarithm of [code]0[/code] returns [code]-inf[/code], while negative values return [code]-nan[/code].
</description>
</method>
<method name="max">
@@ -684,7 +686,9 @@
Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value.
Use a negative [code]delta[/code] value to move away.
[codeblock]
+ move_toward(5, 10, 4) # Returns 9
move_toward(10, 5, 4) # Returns 6
+ move_toward(10, 5, -1.5) # Returns 11.5
[/codeblock]
</description>
</method>
@@ -694,12 +698,17 @@
<argument index="0" name="value" type="int">
</argument>
<description>
- Returns the nearest larger power of 2 for integer [code]value[/code].
+ Returns the nearest equal or larger power of 2 for integer [code]value[/code].
+ In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value &lt;= a[/code] for some non-negative integer [code]n[/code].
[codeblock]
nearest_po2(3) # Returns 4
nearest_po2(4) # Returns 4
nearest_po2(5) # Returns 8
+
+ nearest_po2(0) # Returns 0 (this may not be what you expect)
+ nearest_po2(-1) # Returns 0 (this may not be what you expect)
[/codeblock]
+ [b]WARNING:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2).
</description>
</method>
<method name="ord">
@@ -723,16 +732,17 @@
<argument index="0" name="json" type="String">
</argument>
<description>
- Parse JSON text to a Variant (use [method typeof] to check if it is what you expect).
- Be aware that the JSON specification does not define integer or float types, but only a number type. Therefore, parsing a JSON text will convert all numerical values to [float] types.
- Note that JSON objects do not preserve key order like Godot dictionaries, thus you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements:
+ Parse JSON text to a Variant. (Use [method typeof] to check if the Variant's type is what you expect.)
+ [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, parsing a JSON text will convert all numerical values to [float] types.
+ [b]Note:[/b] JSON objects do not preserve key order like Godot dictionaries, thus, you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements:
[codeblock]
- p = parse_json('["a", "b", "c"]')
- if typeof(p) == TYPE_ARRAY:
- print(p[0]) # Prints a
+ var p = JSON.parse('["hello", "world", "!"]')
+ if typeof(p.result) == TYPE_ARRAY:
+ print(p.result[0]) # Prints "hello"
else:
- print("unexpected results")
+ push_error("Unexpected results.")
[/codeblock]
+ See also [JSON] for an alternative way to parse JSON text.
</description>
</method>
<method name="polar2cartesian">
@@ -756,24 +766,18 @@
<description>
Returns the integer modulus of [code]a/b[/code] that wraps equally in positive and negative.
[codeblock]
- var i = -6
- while i &lt; 5:
- prints(i, posmod(i, 3))
- i += 1
+ for i in range(-3, 4):
+ print("%2.0f %2.0f %2.0f" % [i, i % 3, posmod(i, 3)])
[/codeblock]
Produces:
[codeblock]
- -6 0
- -5 1
- -4 2
- -3 0
- -2 1
- -1 2
- 0 0
- 1 1
- 2 2
- 3 0
- 4 1
+ -3 0 0
+ -2 -2 1
+ -1 -1 2
+ 0 0 0
+ 1 1 1
+ 2 2 2
+ 3 0 0
[/codeblock]
</description>
</method>
@@ -797,11 +801,11 @@
<argument index="0" name="path" type="String">
</argument>
<description>
- Returns a resource from the filesystem that is loaded during script parsing.
- [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path".
+ Returns a [Resource] from the filesystem located at [code]path[/code]. The resource is loaded during script parsing, i.e. is loaded with the script and [method preload] effectively acts as a reference to that resource. Note that the method requires a constant path. If you want to load a resource from a dynamic/variable path, use [method load].
+ [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
[codeblock]
- # Load a scene called main located in the root of the project directory.
- var main = preload("res://main.tscn")
+ # Instance a scene.
+ var diamond = preload("res://diamond.tscn").instance()
[/codeblock]
</description>
</method>
@@ -814,6 +818,7 @@
a = [1, 2, 3]
print("a", "b", a) # Prints ab[1, 2, 3]
[/codeblock]
+ [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed.
</description>
</method>
<method name="print_debug" qualifiers="vararg">
@@ -887,6 +892,7 @@
[codeblock]
push_error("test error") # Prints "test error" to debugger and terminal as error call
[/codeblock]
+ [b]Note:[/b] 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.
</description>
</method>
<method name="push_warning">
@@ -907,7 +913,7 @@
<argument index="0" name="rad" type="float">
</argument>
<description>
- Converts from radians to degrees.
+ Converts an angle expressed in radians to degrees.
[codeblock]
rad2deg(0.523599) # Returns 30
[/codeblock]
@@ -976,27 +982,15 @@
<description>
Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment).
[codeblock]
- for i in range(4):
- print(i)
- for i in range(2, 5):
- print(i)
- for i in range(0, 6, 2):
- print(i)
+ print(range(4))
+ print(range(2, 5))
+ print(range(0, 6, 2))
[/codeblock]
Output:
[codeblock]
- 0
- 1
- 2
- 3
-
- 2
- 3
- 4
-
- 0
- 2
- 4
+ [0, 1, 2, 3]
+ [2, 3, 4]
+ [0, 2, 4]
[/codeblock]
</description>
</method>
@@ -1026,10 +1020,11 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Returns the integral value that is nearest to [code]s[/code], with halfway cases rounded away from zero.
+ Rounds [code]s[/code] to the nearest whole number, with halfway cases rounded away from zero.
[codeblock]
round(2.6) # Returns 3
[/codeblock]
+ See also [method floor], [method ceil], and [method stepify].
</description>
</method>
<method name="seed">
@@ -1091,12 +1086,15 @@
</argument>
<argument index="1" name="to" type="float">
</argument>
- <argument index="2" name="weight" type="float">
+ <argument index="2" name="s" type="float">
</argument>
<description>
- Returns a number smoothly interpolated between the [code]from[/code] and [code]to[/code], based on the [code]weight[/code]. Similar to [method lerp], but interpolates faster at the beginning and slower at the end.
+ Returns the result of smoothly interpolating the value of [code]s[/code] between [code]0[/code] and [code]1[/code], based on the where [code]s[/code] lies with respect to the edges [code]from[/code] and [code]to[/code].
+ The return value is [code]0[/code] if [code]s &lt;= from[/code], and [code]1[/code] if [code]s &gt;= to[/code]. If [code]s[/code] lies between [code]from[/code] and [code]to[/code], the returned value follows an S-shaped curve that maps [code]s[/code] between [code]0[/code] and [code]1[/code].
+ This S-shaped curve is the cubic Hermite interpolator, given by [code]f(s) = 3*s^2 - 2*s^3[/code].
[codeblock]
- smoothstep(0, 2, 0.5) # Returns 0.15
+ smoothstep(0, 2, -5.0) # Returns 0.0
+ smoothstep(0, 2, 0.5) # Returns 0.15625
smoothstep(0, 2, 1.0) # Returns 0.5
smoothstep(0, 2, 2.0) # Returns 1.0
[/codeblock]
@@ -1108,10 +1106,11 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Returns the square root of [code]s[/code].
+ Returns the square root of [code]s[/code], where [code]s[/code] is a non-negative number.
[codeblock]
sqrt(9) # Returns 3
[/codeblock]
+ [b]Note:[/b]Negative values of [code]s[/code] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#.
</description>
</method>
<method name="step_decimals">
@@ -1144,6 +1143,7 @@
stepify(100, 32) # Returns 96
stepify(3.14159, 0.01) # Returns 3.14
[/codeblock]
+ See also [method ceil], [method floor], and [method round].
</description>
</method>
<method name="str" qualifiers="vararg">
@@ -1204,12 +1204,16 @@
<argument index="0" name="var" type="Variant">
</argument>
<description>
- Converts a Variant [code]var[/code] to JSON text and return the result. Useful for serializing data to store or send over the network.
+ Converts a [Variant] [code]var[/code] to JSON text and return the result. Useful for serializing data to store or send over the network.
[codeblock]
+ # Both numbers below are integers.
a = { "a": 1, "b": 2 }
b = to_json(a)
print(b) # {"a":1, "b":2}
+ # Both numbers above are floats, even if they display without any decimal places.
[/codeblock]
+ [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a [Variant] to JSON text will convert all numerical values to [float] types.
+ See also [JSON] for an alternative way to convert a [Variant] to JSON text.
</description>
</method>
<method name="type_exists">
@@ -1252,9 +1256,9 @@
j = to_json([1, 2, 3])
v = validate_json(j)
if not v:
- print("valid")
+ print("Valid JSON.")
else:
- prints("invalid", v)
+ push_error("Invalid JSON: " + v)
[/codeblock]
</description>
</method>
@@ -1312,27 +1316,19 @@
Wraps float [code]value[/code] between [code]min[/code] and [code]max[/code].
Usable for creating loop-alike behavior or infinite surfaces.
[codeblock]
- # a is 0.5
- a = wrapf(10.5, 0.0, 10.0)
- [/codeblock]
- [codeblock]
- # a is 9.5
- a = wrapf(-0.5, 0.0, 10.0)
- [/codeblock]
- [codeblock]
- # Infinite loop between 0.0 and 0.99
- f = wrapf(f + 0.1, 0.0, 1.0)
+ # Infinite loop between 5.0 and 9.9
+ value = wrapf(value + 0.1, 5.0, 10.0)
[/codeblock]
[codeblock]
# Infinite rotation (in radians)
angle = wrapf(angle + 0.1, 0.0, TAU)
[/codeblock]
- [b]Note:[/b] If you just want to wrap between 0.0 and [code]n[/code] (where [code]n[/code] is a positive floating-point value), it is better for performance to use the [method fmod] method like [code]fmod(number, n)[/code].
- [code]wrapf[/code] is more flexible than using the [method fmod] approach by giving the user a simple control over the minimum value. It also fully supports negative numbers, e.g.
[codeblock]
# Infinite rotation (in radians)
angle = wrapf(angle + 0.1, -PI, PI)
[/codeblock]
+ [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method fposmod], so prefer using that instead.
+ [code]wrapf[/code] is more flexible than using the [method fposmod] approach by giving the user control over the minimum value.
</description>
</method>
<method name="wrapi">
@@ -1348,75 +1344,30 @@
Wraps integer [code]value[/code] between [code]min[/code] and [code]max[/code].
Usable for creating loop-alike behavior or infinite surfaces.
[codeblock]
- # a is 0
- a = wrapi(10, 0, 10)
- [/codeblock]
- [codeblock]
- # a is 9
- a = wrapi(-1, 0, 10)
+ # Infinite loop between 5 and 9
+ frame = wrapi(frame + 1, 5, 10)
[/codeblock]
[codeblock]
- # Infinite loop between 0 and 9
- frame = wrapi(frame + 1, 0, 10)
- [/codeblock]
- [b]Note:[/b] If you just want to wrap between 0 and [code]n[/code] (where [code]n[/code] is a positive integer value), it is better for performance to use the modulo operator like [code]number % n[/code].
- [code]wrapi[/code] is more flexible than using the modulo approach by giving the user a simple control over the minimum value. It also fully supports negative numbers, e.g.
- [codeblock]
# result is -2
var result = wrapi(-6, -5, -1)
[/codeblock]
- </description>
- </method>
- <method name="yield">
- <return type="GDScriptFunctionState">
- </return>
- <argument index="0" name="object" type="Object" default="null">
- </argument>
- <argument index="1" name="signal" type="String" default="&quot;&quot;">
- </argument>
- <description>
- Stops the function execution and returns the current suspended state to the calling function.
- From the caller, call [method GDScriptFunctionState.resume] on the state to resume execution. This invalidates the state. Within the resumed function, [code]yield()[/code] returns whatever was passed to the [code]resume()[/code] function call.
- If passed an object and a signal, the execution is resumed when the object emits the given signal. In this case, [code]yield()[/code] returns the argument passed to [code]emit_signal()[/code] if the signal takes only one argument, or an array containing all the arguments passed to [code]emit_signal()[/code] if the signal takes multiple arguments.
- You can also use [code]yield[/code] to wait for a function to finish:
- [codeblock]
- func _ready():
- yield(countdown(), "completed") # waiting for the countdown() function to complete
- print('Ready')
-
- func countdown():
- yield(get_tree(), "idle_frame") # returns a GDScriptFunctionState object to _ready()
- print(3)
- yield(get_tree().create_timer(1.0), "timeout")
- print(2)
- yield(get_tree().create_timer(1.0), "timeout")
- print(1)
- yield(get_tree().create_timer(1.0), "timeout")
-
- # prints:
- # 3
- # 2
- # 1
- # Ready
- [/codeblock]
- When yielding on a function, the [code]completed[/code] signal will be emitted automatically when the function returns. It can, therefore, be used as the [code]signal[/code] parameter of the [code]yield[/code] method to resume.
- In order to yield on a function, the resulting function should also return a [code]GDScriptFunctionState[/code]. Notice [code]yield(get_tree(), "idle_frame")[/code] from the above example.
+ [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method posmod], so prefer using that instead.
+ [code]wrapi[/code] is more flexible than using the [method posmod] approach by giving the user control over the minimum value.
</description>
</method>
</methods>
<constants>
<constant name="PI" value="3.141593">
- Constant that represents how many times the diameter of a circle fits around its perimeter.
+ Constant that represents how many times the diameter of a circle fits around its perimeter. This is equivalent to [code]TAU / 2[/code].
</constant>
<constant name="TAU" value="6.283185">
- The circle constant, the circumference of the unit circle.
+ The circle constant, the circumference of the unit circle in radians.
</constant>
<constant name="INF" value="inf">
- A positive infinity. (For negative infinity, use -INF).
+ Positive infinity. For negative infinity, use -INF.
</constant>
<constant name="NAN" value="nan">
- Macro constant that expands to an expression of type float that represents a NaN.
- The NaN values are used to identify undefined or non-representable values for floating-point elements, such as the square root of negative numbers or the result of 0/0.
+ "Not a Number", an invalid value. [code]NaN[/code] has special properties, including that it is not equal to itself. It is output by some invalid operations, such as dividing zero by zero.
</constant>
</constants>
</class>
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 62ccb93901..631a102130 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
+ <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
</tutorials>
<methods>
<method name="get_as_byte_code" qualifiers="const">
diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml
index 9a73764646..5e369b32d9 100644
--- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml
+++ b/modules/gdscript/doc_classes/GDScriptFunctionState.xml
@@ -4,7 +4,8 @@
State of a function call after yielding.
</brief_description>
<description>
- Calling [method @GDScript.yield] within a function will cause that function to yield and return its current state as an object of this type. The yielded function call can then be resumed later by calling [method resume] on this state object.
+ FIXME: Outdated docs as of GDScript rewrite in 4.0.
+ Calling [code]yield[/code] within a function will cause that function to yield and return its current state as an object of this type. The yielded function call can then be resumed later by calling [method resume] on this state object.
</description>
<tutorials>
</tutorials>
@@ -26,7 +27,7 @@
</argument>
<description>
Resume execution of the yielded function call.
- If handed an argument, return the argument from the [method @GDScript.yield] call in the yielded function call. You can pass e.g. an [Array] to hand multiple arguments.
+ If handed an argument, return the argument from the [code]yield[/code] call in the yielded function call. You can pass e.g. an [Array] to hand multiple arguments.
This function returns what the resumed function call returns, possibly another function state if yielded again.
</description>
</method>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index d0f27b632b..9a3273d201 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -29,36 +29,24 @@
/*************************************************************************/
#include "gdscript_highlighter.h"
+#include "../gdscript.h"
#include "../gdscript_tokenizer.h"
#include "editor/editor_settings.h"
-#include "scene/gui/text_edit.h"
-inline bool _is_symbol(CharType c) {
- return is_symbol(c);
-}
-
-static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
-static bool _is_char(CharType c) {
+static bool _is_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
-static bool _is_number(CharType c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex_symbol(CharType c) {
+static bool _is_hex_symbol(char32_t c) {
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
-static bool _is_bin_symbol(CharType c) {
+static bool _is_bin_symbol(char32_t c) {
return (c == '0' || c == '1');
}
-Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
- Map<int, TextEdit::HighlighterInfo> color_map;
+Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
+ Dictionary color_map;
Type next_type = NONE;
Type current_type = NONE;
@@ -82,36 +70,139 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
Color keyword_color;
Color color;
- int in_region = text_editor->_is_line_in_region(p_line);
- int deregion = 0;
+ color_region_cache[p_line] = -1;
+ int in_region = -1;
+ if (p_line != 0) {
+ if (!color_region_cache.has(p_line - 1)) {
+ get_line_syntax_highlighting(p_line - 1);
+ }
+ in_region = color_region_cache[p_line - 1];
+ }
- const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text_editor->_get_line_color_region_info(p_line);
- const String &str = text_editor->get_line(p_line);
+ const String &str = text_edit->get_line(p_line);
+ const int line_length = str.length();
Color prev_color;
+
+ if (in_region != -1 && str.length() == 0) {
+ color_region_cache[p_line] = in_region;
+ }
for (int j = 0; j < str.length(); j++) {
- TextEdit::HighlighterInfo highlighter_info;
+ Dictionary highlighter_info;
- if (deregion > 0) {
- deregion--;
- if (deregion == 0) {
- in_region = -1;
+ color = font_color;
+ bool is_char = !is_symbol(str[j]);
+ bool is_a_symbol = is_symbol(str[j]);
+ bool is_number = (str[j] >= '0' && str[j] <= '9');
+
+ /* color regions */
+ if (is_a_symbol || in_region != -1) {
+ int from = j;
+ for (; from < line_length; from++) {
+ if (str[from] == '\\') {
+ from++;
+ continue;
+ }
+ break;
}
- }
- if (deregion != 0) {
- if (color != prev_color) {
- prev_color = color;
- highlighter_info.color = color;
- color_map[j] = highlighter_info;
- }
- continue;
- }
+ if (from != line_length) {
+ /* check if we are in entering a region */
+ if (in_region == -1) {
+ for (int c = 0; c < color_regions.size(); c++) {
+ /* check there is enough room */
+ int chars_left = line_length - from;
+ int start_key_length = color_regions[c].start_key.length();
+ int end_key_length = color_regions[c].end_key.length();
+ if (chars_left < start_key_length) {
+ continue;
+ }
+
+ /* search the line */
+ bool match = true;
+ const char32_t *start_key = color_regions[c].start_key.get_data();
+ for (int k = 0; k < start_key_length; k++) {
+ if (start_key[k] != str[from + k]) {
+ match = false;
+ break;
+ }
+ }
+ if (!match) {
+ continue;
+ }
+ in_region = c;
+ from += start_key_length;
+
+ /* check if it's the whole line */
+ if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
+ prev_color = color_regions[in_region].color;
+ highlighter_info["color"] = color_regions[c].color;
+ color_map[j] = highlighter_info;
+
+ j = line_length;
+ if (!color_regions[c].line_only) {
+ color_region_cache[p_line] = c;
+ }
+ }
+ break;
+ }
- color = font_color;
+ if (j == line_length) {
+ continue;
+ }
+ }
+
+ /* if we are in one find the end key */
+ if (in_region != -1) {
+ /* search the line */
+ int region_end_index = -1;
+ int end_key_length = color_regions[in_region].end_key.length();
+ const char32_t *end_key = color_regions[in_region].end_key.get_data();
+ for (; from < line_length; from++) {
+ if (line_length - from < end_key_length) {
+ break;
+ }
+
+ if (!is_symbol(str[from])) {
+ continue;
+ }
+
+ if (str[from] == '\\') {
+ from++;
+ continue;
+ }
+
+ region_end_index = from;
+ for (int k = 0; k < end_key_length; k++) {
+ if (end_key[k] != str[from + k]) {
+ region_end_index = -1;
+ break;
+ }
+ }
+
+ if (region_end_index != -1) {
+ break;
+ }
+ }
+
+ prev_color = color_regions[in_region].color;
+ highlighter_info["color"] = color_regions[in_region].color;
+ color_map[j] = highlighter_info;
+
+ previous_type = REGION;
+ previous_text = "";
+ previous_column = j;
+ j = from + (end_key_length - 1);
+ if (region_end_index == -1) {
+ color_region_cache[p_line] = in_region;
+ }
- bool is_char = _is_text_char(str[j]);
- bool is_symbol = _is_symbol(str[j]);
- bool is_number = _is_number(str[j]);
+ in_region = -1;
+ prev_is_char = false;
+ prev_is_number = false;
+ continue;
+ }
+ }
+ }
// allow ABCDEF in hex notation
if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
@@ -133,7 +224,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
// check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation
if ((str[j] == '.' || str[j] == 'x' || str[j] == 'b' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
is_number = true;
- is_symbol = false;
+ is_a_symbol = false;
is_char = false;
if (str[j] == 'x' && str[j - 1] == '0') {
@@ -151,43 +242,26 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
is_number = false;
}
- if (is_symbol && str[j] != '.' && in_word) {
+ if (is_a_symbol && str[j] != '.' && in_word) {
in_word = false;
}
- if (is_symbol && cri_map.has(j)) {
- const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
-
- if (in_region == -1) {
- if (!cri.end) {
- in_region = cri.region;
- }
- } else {
- TextEdit::ColorRegion cr = text_editor->_get_color_region(cri.region);
- if (in_region == cri.region && !cr.line_only) { //ignore otherwise
- if (cri.end || cr.eq) {
- deregion = cr.eq ? cr.begin_key.length() : cr.end_key.length();
- }
- }
- }
- }
-
if (!is_char) {
in_keyword = false;
}
- if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+ if (!in_keyword && is_char && !prev_is_char) {
int to = j;
- while (to < str.length() && _is_text_char(str[to])) {
+ while (to < str.length() && !is_symbol(str[to])) {
to++;
}
String word = str.substr(j, to - j);
Color col = Color();
- if (text_editor->has_keyword_color(word)) {
- col = text_editor->get_keyword_color(word);
- } else if (text_editor->has_member_color(word)) {
- col = text_editor->get_member_color(word);
+ if (keywords.has(word)) {
+ col = keywords[word];
+ } else if (member_keywords.has(word)) {
+ col = member_keywords[word];
for (int k = j - 1; k >= 0; k--) {
if (str[k] == '.') {
col = Color(); //member indexing not allowed
@@ -206,7 +280,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (!in_function_name && in_word && !in_keyword) {
int k = j;
- while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ while (k < str.length() && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
k++;
}
@@ -217,14 +291,14 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (str[k] == '(') {
in_function_name = true;
- } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::TK_PR_VAR)) {
+ } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR)) {
in_variable_declaration = true;
}
}
if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
int k = j;
- while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ while (k > 0 && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
k--;
}
@@ -233,7 +307,7 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
}
}
- if (is_symbol) {
+ if (is_a_symbol) {
if (in_function_name) {
in_function_args = true;
}
@@ -270,14 +344,11 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (!in_node_path && in_region == -1 && str[j] == '$') {
in_node_path = true;
- } else if (in_region != -1 || (is_symbol && str[j] != '/')) {
+ } else if (in_region != -1 || (is_a_symbol && str[j] != '/')) {
in_node_path = false;
}
- if (in_region >= 0) {
- next_type = REGION;
- color = text_editor->_get_color_region(in_region).color;
- } else if (in_node_path) {
+ if (in_node_path) {
next_type = NODE_PATH;
color = node_path_color;
} else if (in_keyword) {
@@ -289,12 +360,12 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
} else if (in_function_name) {
next_type = FUNCTION;
- if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::TK_PR_FUNCTION)) {
+ if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
color = function_definition_color;
} else {
color = function_color;
}
- } else if (is_symbol) {
+ } else if (is_a_symbol) {
next_type = SYMBOL;
color = symbol_color;
} else if (is_number) {
@@ -335,32 +406,133 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (color != prev_color) {
prev_color = color;
- highlighter_info.color = color;
+ highlighter_info["color"] = color;
color_map[j] = highlighter_info;
}
}
return color_map;
}
-String GDScriptSyntaxHighlighter::get_name() const {
+String GDScriptSyntaxHighlighter::_get_name() const {
return "GDScript";
}
-List<String> GDScriptSyntaxHighlighter::get_supported_languages() {
- List<String> languages;
+Array GDScriptSyntaxHighlighter::_get_supported_languages() const {
+ Array languages;
languages.push_back("GDScript");
return languages;
}
void GDScriptSyntaxHighlighter::_update_cache() {
- font_color = text_editor->get_theme_color("font_color");
- symbol_color = text_editor->get_theme_color("symbol_color");
- function_color = text_editor->get_theme_color("function_color");
- number_color = text_editor->get_theme_color("number_color");
- member_color = text_editor->get_theme_color("member_variable_color");
+ keywords.clear();
+ member_keywords.clear();
+ color_regions.clear();
+ color_region_cache.clear();
+
+ font_color = text_edit->get_theme_color("font_color");
+ symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
+ function_color = EDITOR_GET("text_editor/highlighting/function_color");
+ number_color = EDITOR_GET("text_editor/highlighting/number_color");
+ member_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+
+ /* Engine types. */
+ const Color types_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
+ List<StringName> types;
+ ClassDB::get_class_list(&types);
+ for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
+ String n = E->get();
+ if (n.begins_with("_")) {
+ n = n.substr(1, n.length());
+ }
+ keywords[n] = types_color;
+ }
+
+ /* User types. */
+ const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
+ List<StringName> global_classes;
+ ScriptServer::get_global_class_list(&global_classes);
+ for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
+ keywords[String(E->get())] = usertype_color;
+ }
+
+ /* Autoloads. */
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->value();
+ if (info.is_singleton) {
+ keywords[info.name] = usertype_color;
+ }
+ }
+
+ const GDScriptLanguage *gdscript = GDScriptLanguage::get_singleton();
+
+ /* Core types. */
+ const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+ List<String> core_types;
+ gdscript->get_core_type_words(&core_types);
+ for (List<String>::Element *E = core_types.front(); E; E = E->next()) {
+ keywords[E->get()] = basetype_color;
+ }
+
+ /* Reserved words. */
+ const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ List<String> keyword_list;
+ gdscript->get_reserved_words(&keyword_list);
+ for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
+ keywords[E->get()] = keyword_color;
+ }
+
+ /* Comments */
+ const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+ List<String> comments;
+ gdscript->get_comment_delimiters(&comments);
+ for (List<String>::Element *E = comments.front(); E; E = E->next()) {
+ String comment = E->get();
+ String beg = comment.get_slice(" ", 0);
+ String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
+ add_color_region(beg, end, comment_color, end == "");
+ }
+
+ /* Strings */
+ const Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
+ List<String> strings;
+ gdscript->get_string_delimiters(&strings);
+ for (List<String>::Element *E = strings.front(); E; E = E->next()) {
+ String string = E->get();
+ String beg = string.get_slice(" ", 0);
+ String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
+ add_color_region(beg, end, string_color, end == "");
+ }
- const String text_editor_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
- const bool default_theme = text_editor_color_theme == "Default";
+ const Ref<Script> script = _get_edited_resource();
+ if (script.is_valid()) {
+ /* Member types. */
+ const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ StringName instance_base = script->get_instance_base_type();
+ if (instance_base != StringName()) {
+ List<PropertyInfo> plist;
+ ClassDB::get_property_list(instance_base, &plist);
+ for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+ String name = E->get().name;
+ if (E->get().usage & PROPERTY_USAGE_CATEGORY || E->get().usage & PROPERTY_USAGE_GROUP || E->get().usage & PROPERTY_USAGE_SUBGROUP) {
+ continue;
+ }
+ if (name.find("/") != -1) {
+ continue;
+ }
+ member_keywords[name] = member_variable_color;
+ }
+
+ List<String> clist;
+ ClassDB::get_integer_constant_list(instance_base, &clist);
+ for (List<String>::Element *E = clist.front(); E; E = E->next()) {
+ member_keywords[E->get()] = member_variable_color;
+ }
+ }
+ }
+
+ const String text_edit_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
+ const bool default_theme = text_edit_color_theme == "Default";
if (default_theme || EditorSettings::get_singleton()->is_dark_theme()) {
function_definition_color = Color(0.4, 0.9, 1.0);
@@ -372,7 +544,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
- if (text_editor_color_theme == "Adaptive" || default_theme) {
+ if (text_edit_color_theme == "Adaptive" || default_theme) {
EditorSettings::get_singleton()->set_initial_value(
"text_editor/highlighting/gdscript/function_definition_color",
function_definition_color,
@@ -388,6 +560,36 @@ void GDScriptSyntaxHighlighter::_update_cache() {
type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
}
-SyntaxHighlighter *GDScriptSyntaxHighlighter::create() {
- return memnew(GDScriptSyntaxHighlighter);
+void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
+ for (int i = 0; i < p_start_key.length(); i++) {
+ ERR_FAIL_COND_MSG(!is_symbol(p_start_key[i]), "color regions must start with a symbol");
+ }
+
+ if (p_end_key.length() > 0) {
+ for (int i = 0; i < p_end_key.length(); i++) {
+ ERR_FAIL_COND_MSG(!is_symbol(p_end_key[i]), "color regions must end with a symbol");
+ }
+ }
+
+ int at = 0;
+ for (int i = 0; i < color_regions.size(); i++) {
+ ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "color region with start key '" + p_start_key + "' already exists.");
+ if (p_start_key.length() < color_regions[i].start_key.length()) {
+ at++;
+ }
+ }
+
+ ColorRegion color_region;
+ color_region.color = p_color;
+ color_region.start_key = p_start_key;
+ color_region.end_key = p_end_key;
+ color_region.line_only = p_line_only;
+ color_regions.insert(at, color_region);
+ clear_highlighting_cache();
+}
+
+Ref<EditorSyntaxHighlighter> GDScriptSyntaxHighlighter::_create() const {
+ Ref<GDScriptSyntaxHighlighter> syntax_highlighter;
+ syntax_highlighter.instance();
+ return syntax_highlighter;
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index e652fb1471..49357f3d2e 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -31,10 +31,25 @@
#ifndef GDSCRIPT_HIGHLIGHTER_H
#define GDSCRIPT_HIGHLIGHTER_H
+#include "editor/plugins/script_editor_plugin.h"
#include "scene/gui/text_edit.h"
-class GDScriptSyntaxHighlighter : public SyntaxHighlighter {
+class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
+ GDCLASS(GDScriptSyntaxHighlighter, EditorSyntaxHighlighter)
+
private:
+ struct ColorRegion {
+ Color color;
+ String start_key;
+ String end_key;
+ bool line_only;
+ };
+ Vector<ColorRegion> color_regions;
+ Map<int, int> color_region_cache;
+
+ Dictionary keywords;
+ Dictionary member_keywords;
+
enum Type {
NONE,
REGION,
@@ -59,14 +74,16 @@ private:
Color node_path_color;
Color type_color;
+ void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
+
public:
- static SyntaxHighlighter *create();
+ virtual void _update_cache() override;
+ virtual Dictionary _get_line_syntax_highlighting(int p_line) override;
- virtual void _update_cache();
- virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line);
+ virtual String _get_name() const override;
+ virtual Array _get_supported_languages() const override;
- virtual String get_name() const;
- virtual List<String> get_supported_languages();
+ virtual Ref<EditorSyntaxHighlighter> _create() const override;
};
#endif // GDSCRIPT_HIGHLIGHTER_H
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
new file mode 100644
index 0000000000..944ed859f5
--- /dev/null
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -0,0 +1,351 @@
+/*************************************************************************/
+/* gdscript_translation_parser_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "gdscript_translation_parser_plugin.h"
+
+#include "core/io/resource_loader.h"
+#include "modules/gdscript/gdscript.h"
+
+void GDScriptEditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_extensions) const {
+ GDScriptLanguage::get_singleton()->get_recognized_extensions(r_extensions);
+}
+
+Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) {
+ // Extract all translatable strings using the parsed tree from GDSriptParser.
+ // The strategy is to find all ExpressionNode and AssignmentNode from the tree and extract strings if relevant, i.e
+ // Search strings in ExpressionNode -> CallNode -> tr(), set_text(), set_placeholder() etc.
+ // Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc.
+
+ Error err;
+ RES loaded_res = ResourceLoader::load(p_path, "", false, &err);
+ if (err) {
+ ERR_PRINT("Failed to load " + p_path);
+ return err;
+ }
+
+ ids = r_ids;
+ ids_ctx_plural = r_ids_ctx_plural;
+ Ref<GDScript> gdscript = loaded_res;
+ String source_code = gdscript->get_source_code();
+
+ GDScriptParser parser;
+ err = parser.parse(source_code, p_path, false);
+ if (err != OK) {
+ ERR_PRINT("Failed to parse with GDScript with GDScriptParser.");
+ return err;
+ }
+
+ // Traverse through the parsed tree from GDScriptParser.
+ GDScriptParser::ClassNode *c = parser.get_tree();
+ _traverse_class(c);
+
+ return OK;
+}
+
+void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser::ClassNode *p_class) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &m = p_class->members[i];
+ // There are 7 types of Member, but only class, function and variable can contain translatable strings.
+ switch (m.type) {
+ case GDScriptParser::ClassNode::Member::CLASS:
+ _traverse_class(m.m_class);
+ break;
+ case GDScriptParser::ClassNode::Member::FUNCTION:
+ _traverse_function(m.function);
+ break;
+ case GDScriptParser::ClassNode::Member::VARIABLE:
+ _read_variable(m.variable);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_traverse_function(const GDScriptParser::FunctionNode *p_func) {
+ _traverse_block(p_func->body);
+}
+
+void GDScriptEditorTranslationParserPlugin::_read_variable(const GDScriptParser::VariableNode *p_var) {
+ _assess_expression(p_var->initializer);
+}
+
+void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser::SuiteNode *p_suite) {
+ if (!p_suite) {
+ return;
+ }
+
+ const Vector<GDScriptParser::Node *> &statements = p_suite->statements;
+ for (int i = 0; i < statements.size(); i++) {
+ GDScriptParser::Node *statement = statements[i];
+
+ // Statements with Node type constant, break, continue, pass, breakpoint are skipped because they can't contain translatable strings.
+ switch (statement->type) {
+ case GDScriptParser::Node::VARIABLE:
+ _assess_expression(static_cast<GDScriptParser::VariableNode *>(statement)->initializer);
+ break;
+ case GDScriptParser::Node::IF: {
+ GDScriptParser::IfNode *if_node = static_cast<GDScriptParser::IfNode *>(statement);
+ _assess_expression(if_node->condition);
+ //FIXME : if the elif logic is changed in GDScriptParser, then this probably will have to change as well. See GDScriptParser::TreePrinter::print_if().
+ _traverse_block(if_node->true_block);
+ _traverse_block(if_node->false_block);
+ break;
+ }
+ case GDScriptParser::Node::FOR: {
+ GDScriptParser::ForNode *for_node = static_cast<GDScriptParser::ForNode *>(statement);
+ _assess_expression(for_node->list);
+ _traverse_block(for_node->loop);
+ break;
+ }
+ case GDScriptParser::Node::WHILE: {
+ GDScriptParser::WhileNode *while_node = static_cast<GDScriptParser::WhileNode *>(statement);
+ _assess_expression(while_node->condition);
+ _traverse_block(while_node->loop);
+ break;
+ }
+ case GDScriptParser::Node::MATCH: {
+ GDScriptParser::MatchNode *match_node = static_cast<GDScriptParser::MatchNode *>(statement);
+ _assess_expression(match_node->test);
+ for (int j = 0; j < match_node->branches.size(); j++) {
+ _traverse_block(match_node->branches[j]->block);
+ }
+ break;
+ }
+ case GDScriptParser::Node::RETURN:
+ _assess_expression(static_cast<GDScriptParser::ReturnNode *>(statement)->return_value);
+ break;
+ case GDScriptParser::Node::ASSERT:
+ _assess_expression((static_cast<GDScriptParser::AssertNode *>(statement))->condition);
+ break;
+ case GDScriptParser::Node::ASSIGNMENT:
+ _assess_assignment(static_cast<GDScriptParser::AssignmentNode *>(statement));
+ break;
+ default:
+ if (statement->is_expression()) {
+ _assess_expression(static_cast<GDScriptParser::ExpressionNode *>(statement));
+ }
+ break;
+ }
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_assess_expression(GDScriptParser::ExpressionNode *p_expression) {
+ // Explore all ExpressionNodes to find CallNodes which contain translation strings, such as tr(), set_text() etc.
+ // tr() can be embedded quite deep within multiple ExpressionNodes so need to dig down to search through all ExpressionNodes.
+ if (!p_expression) {
+ return;
+ }
+
+ // ExpressionNode of type await, cast, get_node, identifier, literal, preload, self, subscript, unary are ignored as they can't be CallNode
+ // containing translation strings.
+ switch (p_expression->type) {
+ case GDScriptParser::Node::ARRAY: {
+ GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(p_expression);
+ for (int i = 0; i < array_node->elements.size(); i++) {
+ _assess_expression(array_node->elements[i]);
+ }
+ break;
+ }
+ case GDScriptParser::Node::ASSIGNMENT:
+ _assess_assignment(static_cast<GDScriptParser::AssignmentNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::BINARY_OPERATOR: {
+ GDScriptParser::BinaryOpNode *binary_op_node = static_cast<GDScriptParser::BinaryOpNode *>(p_expression);
+ _assess_expression(binary_op_node->left_operand);
+ _assess_expression(binary_op_node->right_operand);
+ break;
+ }
+ case GDScriptParser::Node::CALL: {
+ GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_expression);
+ _extract_from_call(call_node);
+ for (int i = 0; i < call_node->arguments.size(); i++) {
+ _assess_expression(call_node->arguments[i]);
+ }
+ } break;
+ case GDScriptParser::Node::DICTIONARY: {
+ GDScriptParser::DictionaryNode *dict_node = static_cast<GDScriptParser::DictionaryNode *>(p_expression);
+ for (int i = 0; i < dict_node->elements.size(); i++) {
+ _assess_expression(dict_node->elements[i].key);
+ _assess_expression(dict_node->elements[i].value);
+ }
+ break;
+ }
+ case GDScriptParser::Node::TERNARY_OPERATOR: {
+ GDScriptParser::TernaryOpNode *ternary_op_node = static_cast<GDScriptParser::TernaryOpNode *>(p_expression);
+ _assess_expression(ternary_op_node->condition);
+ _assess_expression(ternary_op_node->true_expr);
+ _assess_expression(ternary_op_node->false_expr);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_assess_assignment(GDScriptParser::AssignmentNode *p_assignment) {
+ // Extract the translatable strings coming from assignments. For example, get_node("Label").text = "____"
+
+ StringName assignee_name;
+ if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
+ assignee_name = static_cast<GDScriptParser::IdentifierNode *>(p_assignment->assignee)->name;
+ } else if (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) {
+ assignee_name = static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->attribute->name;
+ }
+
+ if (assignment_patterns.has(assignee_name) && p_assignment->assigned_value->type == GDScriptParser::Node::LITERAL) {
+ // If the assignment is towards one of the extract patterns (text, hint_tooltip etc.), and the value is a string literal, we collect the string.
+ ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_assignment->assigned_value)->value);
+ } else if (assignee_name == fd_filters && p_assignment->assigned_value->type == GDScriptParser::Node::CALL) {
+ // FileDialog.filters accepts assignment in the form of PackedStringArray. For example,
+ // get_node("FileDialog").filters = PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]).
+
+ GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value);
+ if (call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
+ GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(call_node->arguments[0]);
+
+ // Extract the name in "extension ; name" of PackedStringArray.
+ for (int i = 0; i < array_node->elements.size(); i++) {
+ _extract_fd_literals(array_node->elements[i]);
+ }
+ }
+ } else {
+ // If the assignee is not in extract patterns or the assigned_value is not Literal type, try to see if the assigned_value contains tr().
+ _assess_expression(p_assignment->assigned_value);
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_extract_from_call(GDScriptParser::CallNode *p_call) {
+ // Extract the translatable strings coming from function calls. For example:
+ // tr("___"), get_node("Label").set_text("____"), get_node("LineEdit").set_placeholder("____").
+
+ StringName function_name = p_call->function_name;
+
+ // Variables for extracting tr() and tr_n().
+ Vector<String> id_ctx_plural;
+ id_ctx_plural.resize(3);
+ bool extract_id_ctx_plural = true;
+
+ if (function_name == tr_func) {
+ // Extract from tr(id, ctx).
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ if (p_call->arguments[i]->type == GDScriptParser::Node::LITERAL) {
+ id_ctx_plural.write[i] = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[i])->value;
+ } else {
+ // Avoid adding something like tr("Flying dragon", var_context_level_1). We want to extract both id and context together.
+ extract_id_ctx_plural = false;
+ }
+ }
+ if (extract_id_ctx_plural) {
+ ids_ctx_plural->push_back(id_ctx_plural);
+ }
+ } else if (function_name == trn_func) {
+ // Extract from tr_n(id, plural, n, ctx).
+ Vector<int> indices;
+ indices.push_back(0);
+ indices.push_back(3);
+ indices.push_back(1);
+ for (int i = 0; i < indices.size(); i++) {
+ if (indices[i] >= p_call->arguments.size()) {
+ continue;
+ }
+
+ if (p_call->arguments[indices[i]]->type == GDScriptParser::Node::LITERAL) {
+ id_ctx_plural.write[i] = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[indices[i]])->value;
+ } else {
+ extract_id_ctx_plural = false;
+ }
+ }
+ if (extract_id_ctx_plural) {
+ ids_ctx_plural->push_back(id_ctx_plural);
+ }
+ } else if (first_arg_patterns.has(function_name)) {
+ // Extracting argument with only string literals. In other words, not extracting something like set_text("hello " + some_var).
+ if (p_call->arguments[0]->type == GDScriptParser::Node::LITERAL) {
+ ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[0])->value);
+ }
+ } else if (second_arg_patterns.has(function_name)) {
+ if (p_call->arguments[1]->type == GDScriptParser::Node::LITERAL) {
+ ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[1])->value);
+ }
+ } else if (function_name == fd_add_filter) {
+ // Extract the 'JPE Images' in this example - get_node("FileDialog").add_filter("*.jpg; JPE Images").
+ _extract_fd_literals(p_call->arguments[0]);
+
+ } else if (function_name == fd_set_filter && p_call->arguments[0]->type == GDScriptParser::Node::CALL) {
+ // FileDialog.set_filters() accepts assignment in the form of PackedStringArray. For example,
+ // get_node("FileDialog").set_filters( PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"])).
+
+ GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_call->arguments[0]);
+ if (call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
+ GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(call_node->arguments[0]);
+ for (int i = 0; i < array_node->elements.size(); i++) {
+ _extract_fd_literals(array_node->elements[i]);
+ }
+ }
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_extract_fd_literals(GDScriptParser::ExpressionNode *p_expression) {
+ // Extract the name in "extension ; name".
+
+ if (p_expression->type == GDScriptParser::Node::LITERAL) {
+ String arg_val = String(static_cast<GDScriptParser::LiteralNode *>(p_expression)->value);
+ PackedStringArray arr = arg_val.split(";", true);
+ if (arr.size() != 2) {
+ ERR_PRINT("Argument for setting FileDialog has bad format.");
+ return;
+ }
+ ids->push_back(arr[1].strip_edges());
+ }
+}
+
+GDScriptEditorTranslationParserPlugin::GDScriptEditorTranslationParserPlugin() {
+ assignment_patterns.insert("text");
+ assignment_patterns.insert("placeholder_text");
+ assignment_patterns.insert("hint_tooltip");
+
+ first_arg_patterns.insert("set_text");
+ first_arg_patterns.insert("set_tooltip");
+ first_arg_patterns.insert("set_placeholder");
+ first_arg_patterns.insert("add_tab");
+ first_arg_patterns.insert("add_check_item");
+ first_arg_patterns.insert("add_item");
+ first_arg_patterns.insert("add_multistate_item");
+ first_arg_patterns.insert("add_radio_check_item");
+ first_arg_patterns.insert("add_separator");
+ first_arg_patterns.insert("add_submenu_item");
+
+ second_arg_patterns.insert("set_tab_title");
+ second_arg_patterns.insert("add_icon_check_item");
+ second_arg_patterns.insert("add_icon_item");
+ second_arg_patterns.insert("add_icon_radio_check_item");
+ second_arg_patterns.insert("set_item_text");
+}
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index 3a30f3106c..5ea416d4cc 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* csharp_project.cpp */
+/* gdscript_translation_parser_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,41 +28,46 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "csharp_project.h"
+#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
+#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
-#include "core/io/json.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
-#include "core/os/os.h"
-#include "core/project_settings.h"
+#include "core/set.h"
+#include "editor/editor_translation_parser.h"
+#include "modules/gdscript/gdscript_parser.h"
+#include "modules/regex/regex.h"
-#include "../csharp_script.h"
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../utils/string_utils.h"
-#include "script_class_parser.h"
+class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlugin {
+ GDCLASS(GDScriptEditorTranslationParserPlugin, EditorTranslationParserPlugin);
-namespace CSharpProject {
+ Vector<String> *ids;
+ Vector<Vector<String>> *ids_ctx_plural;
-void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
- if (!GLOBAL_DEF("mono/project/auto_update_project", true))
- return;
+ // List of patterns used for extracting translation strings.
+ StringName tr_func = "tr";
+ StringName trn_func = "tr_n";
+ Set<StringName> assignment_patterns;
+ Set<StringName> first_arg_patterns;
+ Set<StringName> second_arg_patterns;
+ // FileDialog patterns.
+ StringName fd_add_filter = "add_filter";
+ StringName fd_set_filter = "set_filters";
+ StringName fd_filters = "filters";
- GDMonoAssembly *tools_project_editor_assembly = GDMono::get_singleton()->get_tools_project_editor_assembly();
+ void _traverse_class(const GDScriptParser::ClassNode *p_class);
+ void _traverse_function(const GDScriptParser::FunctionNode *p_func);
+ void _traverse_block(const GDScriptParser::SuiteNode *p_suite);
- GDMonoClass *klass = tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ProjectUtils");
+ void _read_variable(const GDScriptParser::VariableNode *p_var);
+ void _assess_expression(GDScriptParser::ExpressionNode *p_expression);
+ void _assess_assignment(GDScriptParser::AssignmentNode *p_assignment);
+ void _extract_from_call(GDScriptParser::CallNode *p_call);
+ void _extract_fd_literals(GDScriptParser::ExpressionNode *p_expression);
- Variant project_path = p_project_path;
- Variant item_type = p_item_type;
- Variant include = p_include;
- const Variant *args[3] = { &project_path, &item_type, &include };
- MonoException *exc = nullptr;
- klass->get_method("AddItemToProjectChecked", 3)->invoke(nullptr, args, &exc);
+public:
+ virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;
+ virtual void get_recognized_extensions(List<String> *r_extensions) const override;
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- ERR_FAIL();
- }
-}
+ GDScriptEditorTranslationParserPlugin();
+};
-} // namespace CSharpProject
+#endif // GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 632407c61f..3519038ae6 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -39,7 +39,11 @@
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/project_settings.h"
+#include "gdscript_analyzer.h"
+#include "gdscript_cache.h"
#include "gdscript_compiler.h"
+#include "gdscript_parser.h"
+#include "gdscript_warning.h"
///////////////////////////
@@ -79,6 +83,17 @@ Object *GDScriptNativeClass::instance() {
return ClassDB::instance(name);
}
+void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error) {
+ GDScript *base = p_script->_base;
+ if (base != nullptr) {
+ _super_implicit_constructor(base, p_instance, r_error);
+ if (r_error.error != Callable::CallError::CALL_OK) {
+ return;
+ }
+ }
+ p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error);
+}
+
GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) {
/* STEP 1, CREATE */
@@ -101,10 +116,8 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
MutexLock lock(GDScriptLanguage::singleton->lock);
instances.insert(instance->owner);
}
- if (p_argcount < 0) {
- return instance;
- }
- initializer->call(instance, p_args, p_argcount, r_error);
+
+ _super_implicit_constructor(this, instance, r_error);
if (r_error.error != Callable::CallError::CALL_OK) {
instance->script = Ref<GDScript>();
instance->owner->set_script_instance(nullptr);
@@ -114,6 +127,22 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
}
ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance.");
}
+
+ if (p_argcount < 0) {
+ return instance;
+ }
+ if (initializer != nullptr) {
+ initializer->call(instance, p_args, p_argcount, r_error);
+ if (r_error.error != Callable::CallError::CALL_OK) {
+ instance->script = Ref<GDScript>();
+ instance->owner->set_script_instance(nullptr);
+ {
+ MutexLock lock(GDScriptLanguage::singleton->lock);
+ instances.erase(p_owner);
+ }
+ ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance.");
+ }
+ }
//@TODO make thread safe
return instance;
}
@@ -382,13 +411,11 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
}
GDScriptParser parser;
- Error err = parser.parse(source, basedir, true, path);
-
- if (err == OK) {
- const GDScriptParser::Node *root = parser.get_parse_tree();
- ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, false);
+ GDScriptAnalyzer analyzer(&parser);
+ Error err = parser.parse(source, path, false);
- const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(root);
+ if (err == OK && analyzer.analyze() == OK) {
+ const GDScriptParser::ClassNode *c = parser.get_tree();
if (base_cache.is_valid()) {
base_cache->inheriters_cache.erase(get_instance_id());
@@ -397,8 +424,8 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
if (c->extends_used) {
String path = "";
- if (String(c->extends_file) != "" && String(c->extends_file) != get_path()) {
- path = c->extends_file;
+ if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) {
+ path = c->extends_path;
if (path.is_rel_path()) {
String base = get_path();
if (base == "" || base.is_rel_path()) {
@@ -407,8 +434,8 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
path = base.get_base_dir().plus_file(path);
}
}
- } else if (c->extends_class.size() != 0) {
- String base = c->extends_class[0];
+ } else if (c->extends.size() != 0) {
+ const StringName &base = c->extends[0];
if (ScriptServer::is_global_class(base)) {
path = ScriptServer::get_global_class_path(base);
@@ -431,20 +458,36 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
members_cache.clear();
member_default_values_cache.clear();
+ _signals.clear();
- for (int i = 0; i < c->variables.size(); i++) {
- if (c->variables[i]._export.type == Variant::NIL) {
- continue;
- }
-
- members_cache.push_back(c->variables[i]._export);
- member_default_values_cache[c->variables[i].identifier] = c->variables[i].default_value;
- }
+ for (int i = 0; i < c->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = c->members[i];
- _signals.clear();
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ if (!member.variable->exported) {
+ continue;
+ }
- for (int i = 0; i < c->_signals.size(); i++) {
- _signals[c->_signals[i].name] = c->_signals[i].arguments;
+ members_cache.push_back(member.variable->export_info);
+ Variant default_value;
+ if (member.variable->initializer && member.variable->initializer->is_constant) {
+ default_value = member.variable->initializer->reduced_value;
+ }
+ member_default_values_cache[member.variable->identifier->name] = default_value;
+ } break;
+ case GDScriptParser::ClassNode::Member::SIGNAL: {
+ // TODO: Cache this in parser to avoid loops like this.
+ Vector<StringName> parameters_names;
+ parameters_names.resize(member.signal->parameters.size());
+ for (int j = 0; j < member.signal->parameters.size(); j++) {
+ parameters_names.write[j] = member.signal->parameters[j]->identifier->name;
+ }
+ _signals[member.signal->identifier->name] = parameters_names;
+ } break;
+ default:
+ break; // Nothing.
+ }
}
} else {
placeholder_fallback_enabled = true;
@@ -553,18 +596,44 @@ Error GDScript::reload(bool p_keep_state) {
return OK;
}
+ {
+ String source_path = path;
+ if (source_path.empty()) {
+ source_path = get_path();
+ }
+ if (!source_path.empty()) {
+ MutexLock lock(GDScriptCache::singleton->lock);
+ if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) {
+ GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this;
+ }
+ }
+ }
+
valid = false;
GDScriptParser parser;
- Error err = parser.parse(source, basedir, false, path);
+ Error err = parser.parse(source, path, false);
+ if (err) {
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
+ }
+ // TODO: Show all error messages.
+ _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ ERR_FAIL_V(ERR_PARSE_ERROR);
+ }
+
+ GDScriptAnalyzer analyzer(&parser);
+ err = analyzer.analyze();
+
if (err) {
if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_error_line(), "Parser Error: " + parser.get_error());
+ GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
- _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_error_line(), ("Parse Error: " + parser.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ // TODO: Show all error messages.
+ _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
ERR_FAIL_V(ERR_PARSE_ERROR);
}
- bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool_script();
+ bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();
GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
@@ -585,7 +654,7 @@ Error GDScript::reload(bool p_keep_state) {
const GDScriptWarning &warning = E->get();
if (EngineDebugger::is_active()) {
Vector<ScriptLanguage::StackInfo> si;
- EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
+ EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
}
}
#endif
@@ -753,83 +822,12 @@ void GDScript::_bind_methods() {
}
Vector<uint8_t> GDScript::get_as_byte_code() const {
- GDScriptTokenizerBuffer tokenizer;
- return tokenizer.parse_code_string(source);
+ return Vector<uint8_t>();
};
+// TODO: Fully remove this. There's not this kind of "bytecode" anymore.
Error GDScript::load_byte_code(const String &p_path) {
- Vector<uint8_t> bytecode;
-
- if (p_path.ends_with("gde")) {
- FileAccess *fa = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!fa, ERR_CANT_OPEN);
-
- FileAccessEncrypted *fae = memnew(FileAccessEncrypted);
- ERR_FAIL_COND_V(!fae, ERR_CANT_OPEN);
-
- Vector<uint8_t> key;
- key.resize(32);
- for (int i = 0; i < key.size(); i++) {
- key.write[i] = script_encryption_key[i];
- }
-
- Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_READ);
-
- if (err) {
- fa->close();
- memdelete(fa);
- memdelete(fae);
-
- ERR_FAIL_COND_V(err, err);
- }
-
- bytecode.resize(fae->get_len());
- fae->get_buffer(bytecode.ptrw(), bytecode.size());
- fae->close();
- memdelete(fae);
-
- } else {
- bytecode = FileAccess::get_file_as_array(p_path);
- }
-
- ERR_FAIL_COND_V(bytecode.size() == 0, ERR_PARSE_ERROR);
- path = p_path;
-
- String basedir = path;
-
- if (basedir == "") {
- basedir = get_path();
- }
-
- if (basedir != "") {
- basedir = basedir.get_base_dir();
- }
-
- valid = false;
- GDScriptParser parser;
- Error err = parser.parse_bytecode(bytecode, basedir, get_path());
- if (err) {
- _err_print_error("GDScript::load_byte_code", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_error_line(), ("Parse Error: " + parser.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
- ERR_FAIL_V(ERR_PARSE_ERROR);
- }
-
- GDScriptCompiler compiler;
- err = compiler.compile(&parser, this);
-
- if (err) {
- _err_print_error("GDScript::load_byte_code", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
- ERR_FAIL_V(ERR_COMPILATION_FAILED);
- }
-
- valid = true;
-
- for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
- _set_subclass_path(E->get(), path);
- }
-
- _init_rpc_methods_properties();
-
- return OK;
+ return ERR_COMPILATION_FAILED;
}
Error GDScript::load_source_code(const String &p_path) {
@@ -1055,6 +1053,10 @@ GDScript::~GDScript() {
memdelete(E->get());
}
+ if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
+ GDScriptCache::remove_script(get_path());
+ }
+
_save_orphaned_subclasses();
#ifdef DEBUG_ENABLED
@@ -1153,6 +1155,32 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
{
+ // Signals.
+ const GDScript *sl = sptr;
+ while (sl) {
+ const Map<StringName, Vector<StringName>>::Element *E = sl->_signals.find(p_name);
+ if (E) {
+ r_ret = Signal(this->owner, E->key());
+ return true; //index found
+ }
+ sl = sl->_base;
+ }
+ }
+
+ {
+ // Methods.
+ const GDScript *sl = sptr;
+ while (sl) {
+ const Map<StringName, GDScriptFunction *>::Element *E = sl->member_functions.find(p_name);
+ if (E) {
+ r_ret = Callable(this->owner, E->key());
+ return true; //index found
+ }
+ sl = sl->_base;
+ }
+ }
+
+ {
const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get);
if (E) {
Variant name = p_name;
@@ -1296,38 +1324,6 @@ Variant GDScriptInstance::call(const StringName &p_method, const Variant **p_arg
return Variant();
}
-void GDScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- GDScript *sptr = script.ptr();
- Callable::CallError ce;
-
- while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
- if (E) {
- E->get()->call(this, p_args, p_argcount, ce);
- }
- sptr = sptr->_base;
- }
-}
-
-void GDScriptInstance::_ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount) {
- if (sptr->_base) {
- _ml_call_reversed(sptr->_base, p_method, p_args, p_argcount);
- }
-
- Callable::CallError ce;
-
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
- if (E) {
- E->get()->call(this, p_args, p_argcount, ce);
- }
-}
-
-void GDScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- if (script.ptr()) {
- _ml_call_reversed(script.ptr(), p_method, p_args, p_argcount);
- }
-}
-
void GDScriptInstance::notification(int p_notification) {
//notification is not virtual, it gets called at ALL levels just like in C.
Variant value = p_notification;
@@ -1827,6 +1823,7 @@ void GDScriptLanguage::frame() {
/* EDITOR FUNCTIONS */
void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
+ // TODO: Add annotations here?
static const char *_reserved_words[] = {
// operators
"and",
@@ -1849,6 +1846,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
// functions
"as",
"assert",
+ "await",
"breakpoint",
"class",
"class_name",
@@ -1856,15 +1854,13 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"is",
"func",
"preload",
- "setget",
"signal",
- "tool",
+ "super",
+ "trait",
"yield",
// var
"const",
"enum",
- "export",
- "onready",
"static",
"var",
// control flow
@@ -1878,12 +1874,6 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"return",
"match",
"while",
- "remote",
- "master",
- "puppet",
- "remotesync",
- "mastersync",
- "puppetsync",
nullptr
};
@@ -1914,10 +1904,11 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
String source = f->get_as_utf8_string();
GDScriptParser parser;
- parser.parse(source, p_path.get_base_dir(), true, p_path, false, nullptr, true);
+ err = parser.parse(source, p_path, false);
- if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) {
- const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree());
+ // TODO: Simplify this code by using the analyzer to get full inheritance.
+ if (err == OK) {
+ const GDScriptParser::ClassNode *c = parser.get_tree();
if (r_icon_path) {
if (c->icon_path.empty() || c->icon_path.is_abs_path()) {
*r_icon_path = c->icon_path;
@@ -1931,15 +1922,15 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
GDScriptParser subparser;
while (subclass) {
if (subclass->extends_used) {
- if (subclass->extends_file) {
- if (subclass->extends_class.size() == 0) {
- get_global_class_name(subclass->extends_file, r_base_type);
+ if (!subclass->extends_path.empty()) {
+ if (subclass->extends.size() == 0) {
+ get_global_class_name(subclass->extends_path, r_base_type);
subclass = nullptr;
break;
} else {
- Vector<StringName> extend_classes = subclass->extends_class;
+ Vector<StringName> extend_classes = subclass->extends;
- FileAccessRef subfile = FileAccess::open(subclass->extends_file, FileAccess::READ);
+ FileAccessRef subfile = FileAccess::open(subclass->extends_path, FileAccess::READ);
if (!subfile) {
break;
}
@@ -1948,25 +1939,26 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (subsource.empty()) {
break;
}
- String subpath = subclass->extends_file;
+ String subpath = subclass->extends_path;
if (subpath.is_rel_path()) {
subpath = path.get_base_dir().plus_file(subpath).simplify_path();
}
- if (OK != subparser.parse(subsource, subpath.get_base_dir(), true, subpath, false, nullptr, true)) {
+ if (OK != subparser.parse(subsource, subpath, false)) {
break;
}
path = subpath;
- if (!subparser.get_parse_tree() || subparser.get_parse_tree()->type != GDScriptParser::Node::TYPE_CLASS) {
- break;
- }
- subclass = static_cast<const GDScriptParser::ClassNode *>(subparser.get_parse_tree());
+ subclass = subparser.get_tree();
while (extend_classes.size() > 0) {
bool found = false;
- for (int i = 0; i < subclass->subclasses.size(); i++) {
- const GDScriptParser::ClassNode *inner_class = subclass->subclasses[i];
- if (inner_class->name == extend_classes[0]) {
+ for (int i = 0; i < subclass->members.size(); i++) {
+ if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
+ }
+
+ const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class;
+ if (inner_class->identifier->name == extend_classes[0]) {
extend_classes.remove(0);
found = true;
subclass = inner_class;
@@ -1979,8 +1971,8 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
}
}
- } else if (subclass->extends_class.size() == 1) {
- *r_base_type = subclass->extends_class[0];
+ } else if (subclass->extends.size() == 1) {
+ *r_base_type = subclass->extends[0];
subclass = nullptr;
} else {
break;
@@ -1991,181 +1983,12 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
}
}
- return c->name;
+ return c->identifier != nullptr ? String(c->identifier->name) : String();
}
return String();
}
-#ifdef DEBUG_ENABLED
-String GDScriptWarning::get_message() const {
-#define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String());
-
- switch (code) {
- case UNASSIGNED_VARIABLE_OP_ASSIGN: {
- CHECK_SYMBOLS(1);
- return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value.";
- } break;
- case UNASSIGNED_VARIABLE: {
- CHECK_SYMBOLS(1);
- return "The variable '" + symbols[0] + "' was used but never assigned a value.";
- } break;
- case UNUSED_VARIABLE: {
- CHECK_SYMBOLS(1);
- return "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'";
- } break;
- case SHADOWED_VARIABLE: {
- CHECK_SYMBOLS(2);
- return "The local variable '" + symbols[0] + "' is shadowing an already-defined variable at line " + symbols[1] + ".";
- } break;
- case UNUSED_CLASS_VARIABLE: {
- CHECK_SYMBOLS(1);
- return "The class variable '" + symbols[0] + "' is declared but never used in the script.";
- } break;
- case UNUSED_ARGUMENT: {
- CHECK_SYMBOLS(2);
- return "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'";
- } break;
- case UNREACHABLE_CODE: {
- CHECK_SYMBOLS(1);
- return "Unreachable code (statement after return) in function '" + symbols[0] + "()'.";
- } break;
- case STANDALONE_EXPRESSION: {
- return "Standalone expression (the line has no effect).";
- } break;
- case VOID_ASSIGNMENT: {
- CHECK_SYMBOLS(1);
- return "Assignment operation, but the function '" + symbols[0] + "()' returns void.";
- } break;
- case NARROWING_CONVERSION: {
- return "Narrowing conversion (float is converted to int and loses precision).";
- } break;
- case FUNCTION_MAY_YIELD: {
- CHECK_SYMBOLS(1);
- return "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead.";
- } break;
- case VARIABLE_CONFLICTS_FUNCTION: {
- CHECK_SYMBOLS(1);
- return "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name.";
- } break;
- case FUNCTION_CONFLICTS_VARIABLE: {
- CHECK_SYMBOLS(1);
- return "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name.";
- } break;
- case FUNCTION_CONFLICTS_CONSTANT: {
- CHECK_SYMBOLS(1);
- return "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name.";
- } break;
- case INCOMPATIBLE_TERNARY: {
- return "Values of the ternary conditional are not mutually compatible.";
- } break;
- case UNUSED_SIGNAL: {
- CHECK_SYMBOLS(1);
- return "The signal '" + symbols[0] + "' is declared but never emitted.";
- } break;
- case RETURN_VALUE_DISCARDED: {
- CHECK_SYMBOLS(1);
- return "The function '" + symbols[0] + "()' returns a value, but this value is never used.";
- } break;
- case PROPERTY_USED_AS_FUNCTION: {
- CHECK_SYMBOLS(2);
- return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?";
- } break;
- case CONSTANT_USED_AS_FUNCTION: {
- CHECK_SYMBOLS(2);
- return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?";
- } break;
- case FUNCTION_USED_AS_PROPERTY: {
- CHECK_SYMBOLS(2);
- return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?";
- } break;
- case INTEGER_DIVISION: {
- return "Integer division, decimal part will be discarded.";
- } break;
- case UNSAFE_PROPERTY_ACCESS: {
- CHECK_SYMBOLS(2);
- return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
- } break;
- case UNSAFE_METHOD_ACCESS: {
- CHECK_SYMBOLS(2);
- return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
- } break;
- case UNSAFE_CAST: {
- CHECK_SYMBOLS(1);
- return "The value is cast to '" + symbols[0] + "' but has an unknown type.";
- } break;
- case UNSAFE_CALL_ARGUMENT: {
- CHECK_SYMBOLS(4);
- return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided";
- } break;
- case DEPRECATED_KEYWORD: {
- CHECK_SYMBOLS(2);
- return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'.";
- } break;
- case STANDALONE_TERNARY: {
- return "Standalone ternary conditional operator: the return value is being discarded.";
- }
- case WARNING_MAX:
- break; // Can't happen, but silences warning
- }
- ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + ".");
-
-#undef CHECK_SYMBOLS
-}
-
-String GDScriptWarning::get_name() const {
- return get_name_from_code(code);
-}
-
-String GDScriptWarning::get_name_from_code(Code p_code) {
- ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String());
-
- static const char *names[] = {
- "UNASSIGNED_VARIABLE",
- "UNASSIGNED_VARIABLE_OP_ASSIGN",
- "UNUSED_VARIABLE",
- "SHADOWED_VARIABLE",
- "UNUSED_CLASS_VARIABLE",
- "UNUSED_ARGUMENT",
- "UNREACHABLE_CODE",
- "STANDALONE_EXPRESSION",
- "VOID_ASSIGNMENT",
- "NARROWING_CONVERSION",
- "FUNCTION_MAY_YIELD",
- "VARIABLE_CONFLICTS_FUNCTION",
- "FUNCTION_CONFLICTS_VARIABLE",
- "FUNCTION_CONFLICTS_CONSTANT",
- "INCOMPATIBLE_TERNARY",
- "UNUSED_SIGNAL",
- "RETURN_VALUE_DISCARDED",
- "PROPERTY_USED_AS_FUNCTION",
- "CONSTANT_USED_AS_FUNCTION",
- "FUNCTION_USED_AS_PROPERTY",
- "INTEGER_DIVISION",
- "UNSAFE_PROPERTY_ACCESS",
- "UNSAFE_METHOD_ACCESS",
- "UNSAFE_CAST",
- "UNSAFE_CALL_ARGUMENT",
- "DEPRECATED_KEYWORD",
- "STANDALONE_TERNARY",
- nullptr
- };
-
- return names[(int)p_code];
-}
-
-GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) {
- for (int i = 0; i < WARNING_MAX; i++) {
- if (get_name_from_code((Code)i) == p_name) {
- return (Code)i;
- }
- }
-
- ERR_FAIL_V_MSG(WARNING_MAX, "Invalid GDScript warning name: " + p_name);
-}
-
-#endif // DEBUG_ENABLED
-
GDScriptLanguage::GDScriptLanguage() {
calls = 0;
ERR_FAIL_COND(singleton);
@@ -2204,7 +2027,7 @@ GDScriptLanguage::GDScriptLanguage() {
GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
- bool default_enabled = !warning.begins_with("unsafe_") && i != GDScriptWarning::UNUSED_CLASS_VARIABLE;
+ bool default_enabled = !warning.begins_with("unsafe_");
GLOBAL_DEF("debug/gdscript/warnings/" + warning, default_enabled);
}
#endif // DEBUG_ENABLED
@@ -2214,7 +2037,33 @@ GDScriptLanguage::~GDScriptLanguage() {
if (_call_stack) {
memdelete_arr(_call_stack);
}
- singleton = nullptr;
+
+ // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit).
+ SelfList<GDScript> *s = script_list.first();
+ while (s) {
+ GDScript *script = s->self();
+ // This ensures the current script is not released before we can check what's the next one
+ // in the list (we can't get the next upfront because we don't know if the reference breaking
+ // will cause it -or any other after it, for that matter- to be released so the next one
+ // is not the same as before).
+ script->reference();
+
+ for (Map<StringName, GDScriptFunction *>::Element *E = script->member_functions.front(); E; E = E->next()) {
+ GDScriptFunction *func = E->get();
+ for (int i = 0; i < func->argument_types.size(); i++) {
+ func->argument_types.write[i].script_type_ref = Ref<Script>();
+ }
+ func->return_type.script_type_ref = Ref<Script>();
+ }
+ for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
+ E->get().data_type.script_type_ref = Ref<Script>();
+ }
+
+ s = s->next();
+ script->unreference();
+ }
+
+ singleton = NULL;
}
void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass) {
@@ -2242,36 +2091,28 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
*r_error = ERR_FILE_CANT_OPEN;
}
- GDScript *script = memnew(GDScript);
-
- Ref<GDScript> scriptres(script);
-
- if (p_path.ends_with(".gde") || p_path.ends_with(".gdc")) {
- script->set_script_path(p_original_path); // script needs this.
- script->set_path(p_original_path);
- Error err = script->load_byte_code(p_path);
- ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load byte code from file '" + p_path + "'.");
-
- } else {
- Error err = script->load_source_code(p_path);
- ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load source code from file '" + p_path + "'.");
+ Error err;
+ Ref<GDScript> script = GDScriptCache::get_full_script(p_path, err);
- script->set_script_path(p_original_path); // script needs this.
- script->set_path(p_original_path);
+ // TODO: Reintroduce binary and encrypted scripts.
- script->reload();
+ if (script.is_null()) {
+ // Don't fail loading because of parsing error.
+ script.instance();
}
+
if (r_error) {
*r_error = OK;
}
- return scriptres;
+ return script;
}
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
- p_extensions->push_back("gdc");
- p_extensions->push_back("gde");
+ // TODO: Reintroduce binary and encrypted scripts.
+ // p_extensions->push_back("gdc");
+ // p_extensions->push_back("gde");
}
bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
@@ -2280,7 +2121,8 @@ bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
- if (el == "gd" || el == "gdc" || el == "gde") {
+ // TODO: Reintroduce binary and encrypted scripts.
+ if (el == "gd" /*|| el == "gdc" || el == "gde"*/) {
return "GDScript";
}
return "";
@@ -2296,7 +2138,7 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
}
GDScriptParser parser;
- if (OK != parser.parse(source, p_path.get_base_dir(), true, p_path, false, nullptr, true)) {
+ if (OK != parser.parse(source, p_path, false)) {
return;
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index e770dc3abd..79317ff846 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -69,6 +69,7 @@ class GDScript : public Script {
friend class GDScriptInstance;
friend class GDScriptFunction;
+ friend class GDScriptAnalyzer;
friend class GDScriptCompiler;
friend class GDScriptFunctions;
friend class GDScriptLanguage;
@@ -104,7 +105,8 @@ class GDScript : public Script {
#endif
Map<StringName, PropertyInfo> member_info;
- GDScriptFunction *initializer; //direct pointer to _init , faster to locate
+ GDScriptFunction *implicit_initializer = nullptr;
+ GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
int subclass_count;
Set<Object *> instances;
@@ -117,6 +119,7 @@ class GDScript : public Script {
SelfList<GDScriptFunctionState>::List pending_func_states;
+ void _super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error);
GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error);
void _set_subclass_path(Ref<GDScript> &p_sc, const String &p_path);
@@ -124,7 +127,7 @@ class GDScript : public Script {
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance *> placeholders;
//void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
- virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
#ifdef DEBUG_ENABLED
@@ -143,15 +146,14 @@ protected:
bool _set(const StringName &p_name, const Variant &p_value);
void _get_property_list(List<PropertyInfo> *p_properties) const;
- Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- //void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
+ Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
static void _bind_methods();
public:
- virtual bool is_valid() const { return valid; }
+ virtual bool is_valid() const override { return valid; }
- bool inherits_script(const Ref<Script> &p_script) const;
+ bool inherits_script(const Ref<Script> &p_script) const override;
const Map<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; }
const Map<StringName, Variant> &get_constants() const { return constants; }
@@ -164,10 +166,10 @@ public:
const Ref<GDScriptNativeClass> &get_native() const { return native; }
const String &get_script_class_name() const { return name; }
- virtual bool has_script_signal(const StringName &p_signal) const;
- virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+ virtual bool has_script_signal(const StringName &p_signal) const override;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override;
- bool is_tool() const { return tool; }
+ bool is_tool() const override { return tool; }
Ref<GDScript> get_base() const;
const Map<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; }
@@ -175,21 +177,21 @@ public:
StringName debug_get_member_by_index(int p_idx) const;
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual bool can_instance() const;
+ virtual bool can_instance() const override;
- virtual Ref<Script> get_base_script() const;
+ virtual Ref<Script> get_base_script() const override;
- virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
- virtual ScriptInstance *instance_create(Object *p_this);
- virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this);
- virtual bool instance_has(const Object *p_this) const;
+ virtual StringName get_instance_base_type() const override; // this may not work in all scripts, will return empty if so
+ virtual ScriptInstance *instance_create(Object *p_this) override;
+ virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override;
+ virtual bool instance_has(const Object *p_this) const override;
- virtual bool has_source_code() const;
- virtual String get_source_code() const;
- virtual void set_source_code(const String &p_code);
- virtual void update_exports();
+ virtual bool has_source_code() const override;
+ virtual String get_source_code() const override;
+ virtual void set_source_code(const String &p_code) override;
+ virtual void update_exports() override;
- virtual Error reload(bool p_keep_state = false);
+ virtual Error reload(bool p_keep_state = false) override;
void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too...
Error load_source_code(const String &p_path);
@@ -197,17 +199,17 @@ public:
Vector<uint8_t> get_as_byte_code() const;
- bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
+ bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
- virtual void get_script_method_list(List<MethodInfo> *p_list) const;
- virtual bool has_method(const StringName &p_method) const;
- virtual MethodInfo get_method_info(const StringName &p_method) const;
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
+ virtual bool has_method(const StringName &p_method) const override;
+ virtual MethodInfo get_method_info(const StringName &p_method) const override;
- virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
- virtual ScriptLanguage *get_language() const;
+ virtual ScriptLanguage *get_language() const override;
- virtual int get_member_line(const StringName &p_member) const {
+ virtual int get_member_line(const StringName &p_member) const override {
#ifdef TOOLS_ENABLED
if (member_lines.has(p_member)) {
return member_lines[p_member];
@@ -216,23 +218,23 @@ public:
return -1;
}
- virtual void get_constants(Map<StringName, Variant> *p_constants);
- virtual void get_members(Set<StringName> *p_members);
+ virtual void get_constants(Map<StringName, Variant> *p_constants) override;
+ virtual void get_members(Set<StringName> *p_members) override;
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual Vector<ScriptNetData> get_rpc_methods() const override;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
+ virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual Vector<ScriptNetData> get_rset_properties() const override;
+ virtual uint16_t get_rset_property_id(const StringName &p_variable) const override;
+ virtual StringName get_rset_property(const uint16_t p_variable_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
#ifdef TOOLS_ENABLED
- virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; }
+ virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
#endif
GDScript();
@@ -256,8 +258,6 @@ class GDScriptInstance : public ScriptInstance {
SelfList<GDScriptFunctionState>::List pending_func_states;
- void _ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount);
-
public:
virtual Object *get_owner() { return owner; }
@@ -269,8 +269,6 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
@@ -301,52 +299,6 @@ public:
~GDScriptInstance();
};
-#ifdef DEBUG_ENABLED
-struct GDScriptWarning {
- enum Code {
- UNASSIGNED_VARIABLE, // Variable used but never assigned
- UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc)
- UNUSED_VARIABLE, // Local variable is declared but never used
- SHADOWED_VARIABLE, // Variable name shadowed by other variable
- UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file
- UNUSED_ARGUMENT, // Function argument is never used
- UNREACHABLE_CODE, // Code after a return statement
- STANDALONE_EXPRESSION, // Expression not assigned to a variable
- VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable
- NARROWING_CONVERSION, // Float value into an integer slot, precision is lost
- FUNCTION_MAY_YIELD, // Typed assign of function call that yields (it may return a function state)
- VARIABLE_CONFLICTS_FUNCTION, // Variable has the same name of a function
- FUNCTION_CONFLICTS_VARIABLE, // Function has the same name of a variable
- FUNCTION_CONFLICTS_CONSTANT, // Function has the same name of a constant
- INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible
- UNUSED_SIGNAL, // Signal is defined but never emitted
- RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used
- PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name
- CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name
- FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name
- INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded
- UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes)
- UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes)
- UNSAFE_CAST, // Cast used in an unknown type
- UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument
- DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced
- STANDALONE_TERNARY, // Return value of ternary expression is discarded
- WARNING_MAX,
- };
-
- Code code = WARNING_MAX;
- Vector<String> symbols;
- int line = -1;
-
- String get_name() const;
- String get_message() const;
- static String get_name_from_code(Code p_code);
- static Code get_code_from_name(const String &p_name);
-
- GDScriptWarning() {}
-};
-#endif // DEBUG_ENABLED
-
class GDScriptLanguage : public ScriptLanguage {
friend class GDScriptFunctionState;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
new file mode 100644
index 0000000000..943a49060f
--- /dev/null
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -0,0 +1,3346 @@
+/*************************************************************************/
+/* gdscript_analyzer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "gdscript_analyzer.h"
+
+#include "core/class_db.h"
+#include "core/hash_map.h"
+#include "core/io/resource_loader.h"
+#include "core/os/file_access.h"
+#include "core/project_settings.h"
+#include "core/script_language.h"
+#include "gdscript.h"
+
+// TODO: Move this to a central location (maybe core?).
+static HashMap<StringName, StringName> underscore_map;
+static const char *underscore_classes[] = {
+ "ClassDB",
+ "Directory",
+ "Engine",
+ "File",
+ "Geometry",
+ "GodotSharp",
+ "JSON",
+ "Marshalls",
+ "Mutex",
+ "OS",
+ "ResourceLoader",
+ "ResourceSaver",
+ "Semaphore",
+ "Thread",
+ "VisualScriptEditor",
+ nullptr,
+};
+static StringName get_real_class_name(const StringName &p_source) {
+ if (underscore_map.empty()) {
+ const char **class_name = underscore_classes;
+ while (*class_name != nullptr) {
+ underscore_map[*class_name] = String("_") + *class_name;
+ class_name++;
+ }
+ }
+ if (underscore_map.has(p_source)) {
+ return underscore_map[p_source];
+ }
+ return p_source;
+}
+
+void GDScriptAnalyzer::cleanup() {
+ underscore_map.clear();
+}
+
+static GDScriptParser::DataType make_callable_type(const MethodInfo &p_info) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::BUILTIN;
+ type.builtin_type = Variant::CALLABLE;
+ type.is_constant = true;
+ type.method_info = p_info;
+ return type;
+}
+
+static GDScriptParser::DataType make_signal_type(const MethodInfo &p_info) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::BUILTIN;
+ type.builtin_type = Variant::SIGNAL;
+ type.is_constant = true;
+ type.method_info = p_info;
+ return type;
+}
+
+static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_name) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::NATIVE;
+ type.builtin_type = Variant::OBJECT;
+ type.is_constant = true;
+ type.native_type = p_class_name;
+ type.is_meta_type = true;
+ return type;
+}
+
+static GDScriptParser::DataType make_native_enum_type(const StringName &p_native_class, const StringName &p_enum_name) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::ENUM;
+ type.builtin_type = Variant::OBJECT;
+ type.is_constant = true;
+ type.is_meta_type = true;
+
+ List<StringName> enum_values;
+ StringName real_native_name = get_real_class_name(p_native_class);
+ ClassDB::get_enum_constants(real_native_name, p_enum_name, &enum_values);
+
+ for (const List<StringName>::Element *E = enum_values.front(); E != nullptr; E = E->next()) {
+ type.enum_values[E->get()] = ClassDB::get_integer_constant(real_native_name, E->get());
+ }
+
+ return type;
+}
+
+static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::BUILTIN;
+ type.builtin_type = p_type;
+ type.is_constant = true;
+ type.is_meta_type = true;
+ return type;
+}
+
+Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) {
+ if (p_class->base_type.is_set()) {
+ // Already resolved
+ return OK;
+ }
+
+ if (p_class == parser->head) {
+ if (p_class->identifier) {
+ p_class->fqcn = p_class->identifier->name;
+ } else {
+ p_class->fqcn = parser->script_path;
+ }
+ } else {
+ p_class->fqcn = p_class->outer->fqcn + "::" + String(p_class->identifier->name);
+ }
+
+ GDScriptParser::DataType result;
+
+ // Set datatype for class.
+ GDScriptParser::DataType class_type;
+ class_type.is_constant = true;
+ class_type.is_meta_type = true;
+ class_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ class_type.kind = GDScriptParser::DataType::CLASS;
+ class_type.class_type = p_class;
+ class_type.script_path = parser->script_path;
+ p_class->set_datatype(class_type);
+
+ if (!p_class->extends_used) {
+ result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.native_type = "Reference";
+ } else {
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+
+ GDScriptParser::DataType base;
+
+ int extends_index = 0;
+
+ if (!p_class->extends_path.empty()) {
+ Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path);
+ if (parser.is_null()) {
+ push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class);
+ return ERR_PARSE_ERROR;
+ }
+
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err != OK) {
+ push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", p_class->extends_path), p_class);
+ return err;
+ }
+
+ base = parser->get_parser()->head->get_datatype();
+ } else {
+ if (p_class->extends.empty()) {
+ return ERR_PARSE_ERROR;
+ }
+ const StringName &name = p_class->extends[extends_index++];
+ base.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+
+ if (ScriptServer::is_global_class(name)) {
+ String base_path = ScriptServer::get_global_class_path(name);
+
+ if (base_path == parser->script_path) {
+ base = parser->head->get_datatype();
+ } else {
+ Ref<GDScriptParserRef> parser = get_parser_for(base_path);
+ if (parser.is_null()) {
+ push_error(vformat(R"(Could not resolve super class "%s".)", name), p_class);
+ return ERR_PARSE_ERROR;
+ }
+
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err != OK) {
+ push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
+ return err;
+ }
+ base = parser->get_parser()->head->get_datatype();
+ }
+ } else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
+ const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name);
+ if (info.path.get_extension().to_lower() != ".gd") {
+ push_error(vformat(R"(Singleton %s is not a GDScript.)", info.name), p_class);
+ return ERR_PARSE_ERROR;
+ }
+
+ Ref<GDScriptParserRef> parser = get_parser_for(info.path);
+ if (parser.is_null()) {
+ push_error(vformat(R"(Could not parse singleton from "%s".)", info.path), p_class);
+ return ERR_PARSE_ERROR;
+ }
+
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err != OK) {
+ push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
+ return err;
+ }
+ } else if (class_exists(name) && ClassDB::can_instance(get_real_class_name(name))) {
+ base.kind = GDScriptParser::DataType::NATIVE;
+ base.native_type = name;
+ } else {
+ // Look for other classes in script.
+ GDScriptParser::ClassNode *look_class = p_class;
+ bool found = false;
+ while (look_class != nullptr) {
+ if (look_class->identifier && look_class->identifier->name == name) {
+ if (!look_class->get_datatype().is_set()) {
+ Error err = resolve_inheritance(look_class, false);
+ if (err) {
+ return err;
+ }
+ }
+ base = look_class->get_datatype();
+ found = true;
+ break;
+ }
+ if (look_class->members_indices.has(name) && look_class->get_member(name).type == GDScriptParser::ClassNode::Member::CLASS) {
+ GDScriptParser::ClassNode::Member member = look_class->get_member(name);
+ if (!member.m_class->get_datatype().is_set()) {
+ Error err = resolve_inheritance(member.m_class, false);
+ if (err) {
+ return err;
+ }
+ }
+ base = member.m_class->get_datatype();
+ found = true;
+ break;
+ }
+ look_class = look_class->outer;
+ }
+
+ if (!found) {
+ push_error(vformat(R"(Could not find base class "%s".)", name), p_class);
+ return ERR_PARSE_ERROR;
+ }
+ }
+ }
+
+ for (int index = extends_index; index < p_class->extends.size(); index++) {
+ if (base.kind != GDScriptParser::DataType::CLASS) {
+ push_error(R"(Super type "%s" is not a GDScript. Cannot get nested types.)", p_class);
+ return ERR_PARSE_ERROR;
+ }
+
+ // TODO: Extends could use identifier nodes. That way errors can be pointed out properly and it can be used here.
+ GDScriptParser::IdentifierNode *id = parser->alloc_node<GDScriptParser::IdentifierNode>();
+ id->name = p_class->extends[index];
+
+ reduce_identifier_from_base(id, &base);
+
+ GDScriptParser::DataType id_type = id->get_datatype();
+ if (!id_type.is_set()) {
+ push_error(vformat(R"(Could not find type "%s" under base "%s".)", id->name, base.to_string()), p_class);
+ }
+
+ base = id_type;
+ }
+
+ result = base;
+ }
+
+ if (!result.is_set()) {
+ // TODO: More specific error messages.
+ push_error(vformat(R"(Could not resolve inheritance for class "%s".)", p_class->identifier == nullptr ? "<main>" : p_class->identifier->name), p_class);
+ return ERR_PARSE_ERROR;
+ }
+
+ // Check for cyclic inheritance.
+ const GDScriptParser::ClassNode *base_class = result.class_type;
+ while (base_class) {
+ if (base_class->fqcn == p_class->fqcn) {
+ push_error("Cyclic inheritance.", p_class);
+ return ERR_PARSE_ERROR;
+ }
+ base_class = base_class->base_type.class_type;
+ }
+
+ p_class->base_type = result;
+ class_type.native_type = result.native_type;
+ p_class->set_datatype(class_type);
+
+ if (p_recursive) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {
+ Error err = resolve_inheritance(p_class->members[i].m_class, true);
+ if (err) {
+ return err;
+ }
+ }
+ }
+ }
+
+ return OK;
+}
+
+GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::TypeNode *p_type) {
+ GDScriptParser::DataType result;
+
+ if (p_type == nullptr) {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ return result;
+ }
+
+ result.type_source = result.ANNOTATED_EXPLICIT;
+ result.builtin_type = Variant::OBJECT;
+
+ if (p_type->type_chain.empty()) {
+ // void.
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = Variant::NIL;
+ p_type->set_datatype(result);
+ return result;
+ }
+
+ StringName first = p_type->type_chain[0]->name;
+
+ if (first == "Variant") {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ if (p_type->type_chain.size() > 1) {
+ push_error(R"("Variant" type don't contain nested types.)", p_type->type_chain[1]);
+ return GDScriptParser::DataType();
+ }
+ return result;
+ }
+
+ if (first == "Object") {
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.native_type = "Object";
+ if (p_type->type_chain.size() > 1) {
+ push_error(R"("Object" type don't contain nested types.)", p_type->type_chain[1]);
+ return GDScriptParser::DataType();
+ }
+ return result;
+ }
+
+ if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
+ // Built-in types.
+ if (p_type->type_chain.size() > 1) {
+ push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]);
+ return GDScriptParser::DataType();
+ }
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = GDScriptParser::get_builtin_type(first);
+ } else if (class_exists(first)) {
+ // Native engine classes.
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.native_type = first;
+ } else if (ScriptServer::is_global_class(first)) {
+ if (parser->script_path == ScriptServer::get_global_class_path(first)) {
+ result = parser->head->get_datatype();
+ } else {
+ Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(first));
+ if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
+ return GDScriptParser::DataType();
+ }
+ result = ref->get_parser()->head->get_datatype();
+ }
+ } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
+ const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
+ Ref<GDScriptParserRef> ref = get_parser_for(autoload.path);
+ if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type);
+ return GDScriptParser::DataType();
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else if (ClassDB::has_enum(get_real_class_name(parser->current_class->base_type.native_type), first)) {
+ // Native enum in current class.
+ result = make_native_enum_type(parser->current_class->base_type.native_type, first);
+ } else {
+ // Classes in current scope.
+ GDScriptParser::ClassNode *script_class = parser->current_class;
+ bool found = false;
+ while (!found && script_class != nullptr) {
+ if (script_class->identifier && script_class->identifier->name == first) {
+ result = script_class->get_datatype();
+ found = true;
+ break;
+ }
+ if (script_class->members_indices.has(first)) {
+ GDScriptParser::ClassNode::Member member = script_class->members[script_class->members_indices[first]];
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::CLASS:
+ result = member.m_class->get_datatype();
+ found = true;
+ break;
+ case GDScriptParser::ClassNode::Member::ENUM:
+ result = member.m_enum->get_datatype();
+ found = true;
+ break;
+ case GDScriptParser::ClassNode::Member::CONSTANT:
+ if (member.constant->get_datatype().is_meta_type) {
+ result = member.constant->get_datatype();
+ found = true;
+ break;
+ }
+ [[fallthrough]];
+ default:
+ push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type);
+ return GDScriptParser::DataType();
+ }
+ }
+ script_class = script_class->outer;
+ }
+ }
+ if (!result.is_set()) {
+ push_error(vformat(R"("%s" was not found in the current scope.)", first), p_type);
+ result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
+ return result;
+ }
+
+ if (p_type->type_chain.size() > 1) {
+ if (result.kind == GDScriptParser::DataType::CLASS) {
+ for (int i = 1; i < p_type->type_chain.size(); i++) {
+ GDScriptParser::DataType base = result;
+ reduce_identifier_from_base(p_type->type_chain[i], &base);
+ result = p_type->type_chain[i]->get_datatype();
+ if (!result.is_set()) {
+ push_error(vformat(R"(Could not find type "%s" under base "%s".)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]);
+ result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
+ return result;
+ } else if (!result.is_meta_type) {
+ push_error(vformat(R"(Member "%s" under base "%s" is not a valid type.)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]);
+ result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
+ return result;
+ }
+ }
+ } else if (result.kind == GDScriptParser::DataType::NATIVE) {
+ // Only enums allowed for native.
+ if (ClassDB::has_enum(get_real_class_name(result.native_type), p_type->type_chain[1]->name)) {
+ if (p_type->type_chain.size() > 2) {
+ push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
+ } else {
+ result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name);
+ }
+ }
+ } else {
+ push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]);
+ result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
+ return result;
+ }
+ }
+
+ p_type->set_datatype(result);
+ return result;
+}
+
+void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class) {
+ if (p_class->resolved_interface) {
+ return;
+ }
+ p_class->resolved_interface = true;
+
+ GDScriptParser::ClassNode *previous_class = parser->current_class;
+ parser->current_class = p_class;
+
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ GDScriptParser::DataType datatype;
+ datatype.kind = GDScriptParser::DataType::VARIANT;
+ datatype.type_source = GDScriptParser::DataType::UNDETECTED;
+
+ if (member.variable->initializer != nullptr) {
+ member.variable->set_datatype(datatype); // Allow recursive usage.
+ reduce_expression(member.variable->initializer);
+ datatype = member.variable->initializer->get_datatype();
+ if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
+ datatype.type_source = GDScriptParser::DataType::INFERRED;
+ }
+ }
+
+ if (member.variable->datatype_specifier != nullptr) {
+ datatype = resolve_datatype(member.variable->datatype_specifier);
+ datatype.is_meta_type = false;
+
+ if (member.variable->initializer != nullptr) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) {
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(member.variable->initializer);
+ }
+ } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
+#ifdef DEBUG_ENABLED
+ parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+ }
+ if (member.variable->initializer->get_datatype().is_variant()) {
+ // TODO: Warn unsafe assign.
+ mark_node_unsafe(member.variable->initializer);
+ }
+ }
+ } else if (member.variable->infer_datatype) {
+ if (member.variable->initializer == nullptr) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
+ } else if (!datatype.is_set() || datatype.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.is_variant()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.builtin_type == Variant::NIL) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
+ }
+ datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ }
+
+ datatype.is_constant = false;
+ member.variable->set_datatype(datatype);
+ if (!datatype.has_no_type()) {
+ // TODO: Move this out into a routine specific to validate annotations.
+ if (member.variable->export_info.hint == PROPERTY_HINT_TYPE_STRING) {
+ // @export annotation.
+ switch (datatype.kind) {
+ case GDScriptParser::DataType::BUILTIN:
+ member.variable->export_info.hint_string = Variant::get_type_name(datatype.builtin_type);
+ break;
+ case GDScriptParser::DataType::NATIVE:
+ if (ClassDB::is_parent_class(get_real_class_name(datatype.native_type), "Resource")) {
+ member.variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ member.variable->export_info.hint_string = get_real_class_name(datatype.native_type);
+ } else {
+ push_error(R"(Export type can only be built-in or a resource.)", member.variable);
+ }
+ break;
+ default:
+ // TODO: Allow custom user resources.
+ push_error(R"(Export type can only be built-in or a resource.)", member.variable);
+ break;
+ }
+ }
+ }
+ } break;
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ reduce_expression(member.constant->initializer);
+
+ GDScriptParser::DataType datatype = member.constant->get_datatype();
+ if (member.constant->initializer) {
+ if (!member.constant->initializer->is_constant) {
+ push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer);
+ }
+
+ if (member.constant->datatype_specifier != nullptr) {
+ datatype = resolve_datatype(member.constant->datatype_specifier);
+ datatype.is_meta_type = false;
+
+ if (!is_type_compatible(datatype, member.constant->initializer->get_datatype(), true)) {
+ push_error(vformat(R"(Value of type "%s" cannot be initialized to constant of type "%s".)", member.constant->initializer->get_datatype().to_string(), datatype.to_string()), member.constant->initializer);
+ } else if (datatype.builtin_type == Variant::INT && member.constant->initializer->get_datatype().builtin_type == Variant::FLOAT) {
+#ifdef DEBUG_ENABLED
+ parser->push_warning(member.constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+ }
+ }
+ }
+ datatype.is_constant = true;
+
+ member.constant->set_datatype(datatype);
+ } break;
+ case GDScriptParser::ClassNode::Member::SIGNAL: {
+ for (int j = 0; j < member.signal->parameters.size(); j++) {
+ GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier);
+ signal_type.is_meta_type = false;
+ member.signal->parameters[j]->set_datatype(signal_type);
+ }
+ // TODO: Make MethodInfo from signal.
+ GDScriptParser::DataType signal_type;
+ signal_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ signal_type.kind = GDScriptParser::DataType::BUILTIN;
+ signal_type.builtin_type = Variant::SIGNAL;
+
+ member.signal->set_datatype(signal_type);
+ } break;
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ GDScriptParser::DataType enum_type;
+ enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ enum_type.kind = GDScriptParser::DataType::ENUM;
+ enum_type.builtin_type = Variant::DICTIONARY;
+ enum_type.enum_type = member.m_enum->identifier->name;
+ enum_type.native_type = p_class->fqcn + "." + member.m_enum->identifier->name;
+ enum_type.is_meta_type = true;
+ enum_type.is_constant = true;
+
+ // Enums can't be nested, so we can safely override this.
+ current_enum = member.m_enum;
+
+ for (int j = 0; j < member.m_enum->values.size(); j++) {
+ GDScriptParser::EnumNode::Value &element = member.m_enum->values.write[j];
+
+ if (element.custom_value) {
+ reduce_expression(element.custom_value);
+ if (!element.custom_value->is_constant) {
+ push_error(R"(Enum values must be constant.)", element.custom_value);
+ } else if (element.custom_value->reduced_value.get_type() != Variant::INT) {
+ push_error(R"(Enum values must be integers.)", element.custom_value);
+ } else {
+ element.value = element.custom_value->reduced_value;
+ element.resolved = true;
+ }
+ } else {
+ if (element.index > 0) {
+ element.value = element.parent_enum->values[element.index - 1].value + 1;
+ } else {
+ element.value = 0;
+ }
+ element.resolved = true;
+ }
+
+ enum_type.enum_values[element.identifier->name] = element.value;
+ }
+
+ current_enum = nullptr;
+
+ member.m_enum->set_datatype(enum_type);
+ } break;
+ case GDScriptParser::ClassNode::Member::FUNCTION:
+ resolve_function_signature(member.function);
+ break;
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ if (member.enum_value.custom_value) {
+ current_enum = member.enum_value.parent_enum;
+ reduce_expression(member.enum_value.custom_value);
+ current_enum = nullptr;
+
+ if (!member.enum_value.custom_value->is_constant) {
+ push_error(R"(Enum values must be constant.)", member.enum_value.custom_value);
+ } else if (member.enum_value.custom_value->reduced_value.get_type() != Variant::INT) {
+ push_error(R"(Enum values must be integers.)", member.enum_value.custom_value);
+ } else {
+ member.enum_value.value = member.enum_value.custom_value->reduced_value;
+ member.enum_value.resolved = true;
+ }
+ } else {
+ if (member.enum_value.index > 0) {
+ member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1;
+ } else {
+ member.enum_value.value = 0;
+ }
+ member.enum_value.resolved = true;
+ }
+ // Also update the original references.
+ member.enum_value.parent_enum->values.write[member.enum_value.index] = member.enum_value;
+ p_class->members.write[i].enum_value = member.enum_value;
+ } break;
+ case GDScriptParser::ClassNode::Member::CLASS:
+ break; // Done later.
+ case GDScriptParser::ClassNode::Member::UNDEFINED:
+ ERR_PRINT("Trying to resolve undefined member.");
+ break;
+ }
+ }
+
+ // Recurse nested classes.
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
+ }
+
+ resolve_class_interface(member.m_class);
+ }
+
+ parser->current_class = previous_class;
+}
+
+void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
+ if (p_class->resolved_body) {
+ return;
+ }
+ p_class->resolved_body = true;
+
+ GDScriptParser::ClassNode *previous_class = parser->current_class;
+ parser->current_class = p_class;
+
+ // Do functions now.
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {
+ continue;
+ }
+
+ resolve_function_body(member.function);
+ }
+
+ parser->current_class = previous_class;
+
+ // Recurse nested classes.
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
+ }
+
+ resolve_class_body(member.m_class);
+ }
+
+ // Check unused variables.
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) {
+ continue;
+ }
+#ifdef DEBUG_ENABLED
+ if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
+ parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
+ }
+#endif
+ }
+}
+
+void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
+ ERR_FAIL_COND_MSG(p_node == nullptr, "Trying to resolve type of a null node.");
+
+ switch (p_node->type) {
+ case GDScriptParser::Node::NONE:
+ break; // Unreachable.
+ case GDScriptParser::Node::CLASS:
+ resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node));
+ resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node));
+ break;
+ case GDScriptParser::Node::CONSTANT:
+ resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node));
+ break;
+ case GDScriptParser::Node::FOR:
+ resolve_for(static_cast<GDScriptParser::ForNode *>(p_node));
+ break;
+ case GDScriptParser::Node::FUNCTION:
+ resolve_function_signature(static_cast<GDScriptParser::FunctionNode *>(p_node));
+ resolve_function_body(static_cast<GDScriptParser::FunctionNode *>(p_node));
+ break;
+ case GDScriptParser::Node::IF:
+ resolve_if(static_cast<GDScriptParser::IfNode *>(p_node));
+ break;
+ case GDScriptParser::Node::SUITE:
+ resolve_suite(static_cast<GDScriptParser::SuiteNode *>(p_node));
+ break;
+ case GDScriptParser::Node::VARIABLE:
+ resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node));
+ break;
+ case GDScriptParser::Node::WHILE:
+ resolve_while(static_cast<GDScriptParser::WhileNode *>(p_node));
+ break;
+ case GDScriptParser::Node::ANNOTATION:
+ resolve_annotation(static_cast<GDScriptParser::AnnotationNode *>(p_node));
+ break;
+ case GDScriptParser::Node::ASSERT:
+ resolve_assert(static_cast<GDScriptParser::AssertNode *>(p_node));
+ break;
+ case GDScriptParser::Node::MATCH:
+ resolve_match(static_cast<GDScriptParser::MatchNode *>(p_node));
+ break;
+ case GDScriptParser::Node::MATCH_BRANCH:
+ resolve_match_branch(static_cast<GDScriptParser::MatchBranchNode *>(p_node), nullptr);
+ break;
+ case GDScriptParser::Node::PARAMETER:
+ resolve_parameter(static_cast<GDScriptParser::ParameterNode *>(p_node));
+ break;
+ case GDScriptParser::Node::PATTERN:
+ resolve_match_pattern(static_cast<GDScriptParser::PatternNode *>(p_node), nullptr);
+ break;
+ case GDScriptParser::Node::RETURN:
+ resolve_return(static_cast<GDScriptParser::ReturnNode *>(p_node));
+ break;
+ case GDScriptParser::Node::TYPE:
+ resolve_datatype(static_cast<GDScriptParser::TypeNode *>(p_node));
+ break;
+ // Resolving expression is the same as reducing them.
+ case GDScriptParser::Node::ARRAY:
+ case GDScriptParser::Node::ASSIGNMENT:
+ case GDScriptParser::Node::AWAIT:
+ case GDScriptParser::Node::BINARY_OPERATOR:
+ case GDScriptParser::Node::CALL:
+ case GDScriptParser::Node::CAST:
+ case GDScriptParser::Node::DICTIONARY:
+ case GDScriptParser::Node::GET_NODE:
+ case GDScriptParser::Node::IDENTIFIER:
+ case GDScriptParser::Node::LITERAL:
+ case GDScriptParser::Node::PRELOAD:
+ case GDScriptParser::Node::SELF:
+ case GDScriptParser::Node::SUBSCRIPT:
+ case GDScriptParser::Node::TERNARY_OPERATOR:
+ case GDScriptParser::Node::UNARY_OPERATOR:
+ reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node));
+ break;
+ case GDScriptParser::Node::BREAK:
+ case GDScriptParser::Node::BREAKPOINT:
+ case GDScriptParser::Node::CONTINUE:
+ case GDScriptParser::Node::ENUM:
+ case GDScriptParser::Node::PASS:
+ case GDScriptParser::Node::SIGNAL:
+ // Nothing to do.
+ break;
+ }
+}
+
+void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_annotation) {
+ // TODO: Add second validation function for annotations, so they can use checked types.
+}
+
+void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function) {
+ if (p_function->resolved_signature) {
+ return;
+ }
+ p_function->resolved_signature = true;
+
+ GDScriptParser::FunctionNode *previous_function = parser->current_function;
+ parser->current_function = p_function;
+
+ for (int i = 0; i < p_function->parameters.size(); i++) {
+ resolve_parameter(p_function->parameters[i]);
+#ifdef DEBUG_ENABLED
+ if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) {
+ parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name);
+ }
+ is_shadowing(p_function->parameters[i]->identifier, "function parameter");
+#endif
+ }
+
+ if (p_function->identifier->name == "_init") {
+ // Constructor.
+ GDScriptParser::DataType return_type = parser->current_class->get_datatype();
+ return_type.is_meta_type = false;
+ p_function->set_datatype(return_type);
+ if (p_function->return_type) {
+ push_error("Constructor cannot have an explicit return type.", p_function->return_type);
+ }
+ } else {
+ GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
+ p_function->set_datatype(return_type);
+ }
+
+ parser->current_function = previous_function;
+}
+
+void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_function) {
+ if (p_function->resolved_body) {
+ return;
+ }
+ p_function->resolved_body = true;
+
+ GDScriptParser::FunctionNode *previous_function = parser->current_function;
+ parser->current_function = p_function;
+
+ resolve_suite(p_function->body);
+
+ GDScriptParser::DataType return_type = p_function->body->get_datatype();
+
+ if (p_function->get_datatype().has_no_type() && return_type.is_set()) {
+ // Use the suite inferred type if return isn't explicitly set.
+ return_type.type_source = GDScriptParser::DataType::INFERRED;
+ p_function->set_datatype(p_function->body->get_datatype());
+ } else if (p_function->get_datatype().is_hard_type() && (p_function->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_function->get_datatype().builtin_type != Variant::NIL)) {
+ if (!p_function->body->has_return && p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init) {
+ push_error(R"(Not all code paths return a value.)", p_function);
+ }
+ }
+
+ parser->current_function = previous_function;
+}
+
+void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement) {
+ if (p_statement == nullptr) {
+ return;
+ }
+ switch (p_statement->type) {
+ case GDScriptParser::Node::IF:
+ case GDScriptParser::Node::FOR:
+ case GDScriptParser::Node::MATCH:
+ case GDScriptParser::Node::PATTERN:
+ case GDScriptParser::Node::RETURN:
+ case GDScriptParser::Node::WHILE:
+ // Use return or nested suite type as this suite type.
+ if (p_suite->get_datatype().is_set() && (p_suite->get_datatype() != p_statement->get_datatype())) {
+ // Mixed types.
+ // TODO: This could use the common supertype instead.
+ p_suite->datatype.kind = GDScriptParser::DataType::VARIANT;
+ p_suite->datatype.type_source = GDScriptParser::DataType::UNDETECTED;
+ } else {
+ p_suite->set_datatype(p_statement->get_datatype());
+ p_suite->datatype.type_source = GDScriptParser::DataType::INFERRED;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
+ for (int i = 0; i < p_suite->statements.size(); i++) {
+ GDScriptParser::Node *stmt = p_suite->statements[i];
+ resolve_node(stmt);
+ decide_suite_type(p_suite, stmt);
+ }
+}
+
+void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) {
+ reduce_expression(p_if->condition);
+
+ resolve_suite(p_if->true_block);
+ p_if->set_datatype(p_if->true_block->get_datatype());
+
+ if (p_if->false_block != nullptr) {
+ resolve_suite(p_if->false_block);
+ decide_suite_type(p_if, p_if->false_block);
+ }
+}
+
+void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
+ bool list_resolved = false;
+
+ // Optimize constant range() call to not allocate an array.
+ // Use int, Vector2, Vector3 instead, which also can be used as range iterators.
+ if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) {
+ GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list);
+ GDScriptParser::Node::Type callee_type = call->get_callee_type();
+ if (callee_type == GDScriptParser::Node::IDENTIFIER) {
+ GDScriptParser::IdentifierNode *callee = static_cast<GDScriptParser::IdentifierNode *>(call->callee);
+ if (callee->name == "range") {
+ list_resolved = true;
+ if (call->arguments.size() < 1) {
+ push_error(R"*(Invalid call for "range()" function. Expected at least 1 argument, none given.)*", call->callee);
+ } else if (call->arguments.size() > 3) {
+ push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call->callee);
+ } else {
+ // Now we can optimize it.
+ bool all_is_constant = true;
+ Vector<Variant> args;
+ args.resize(call->arguments.size());
+ for (int i = 0; i < call->arguments.size(); i++) {
+ reduce_expression(call->arguments[i]);
+
+ if (!call->arguments[i]->is_constant) {
+ all_is_constant = false;
+ } else {
+ args.write[i] = call->arguments[i]->reduced_value;
+ }
+
+ GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype();
+ if (arg_type.kind != GDScriptParser::DataType::BUILTIN) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ }
+ }
+
+ Variant reduced;
+
+ if (all_is_constant) {
+ switch (args.size()) {
+ case 1:
+ reduced = args[0];
+ break;
+ case 2:
+ reduced = Vector2i(args[0], args[1]);
+ break;
+ case 3:
+ reduced = Vector3i(args[0], args[1], args[2]);
+ break;
+ }
+ p_for->list->is_constant = true;
+ p_for->list->reduced_value = reduced;
+ }
+ }
+
+ GDScriptParser::DataType list_type;
+ list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ list_type.kind = GDScriptParser::DataType::BUILTIN;
+ list_type.builtin_type = Variant::ARRAY;
+ p_for->list->set_datatype(list_type);
+ }
+ }
+ }
+
+ if (!list_resolved) {
+ resolve_node(p_for->list);
+ }
+
+ // TODO: If list is a typed array, the variable should be an element.
+ // Also applicable for constant range() (so variable is int or float).
+
+ resolve_suite(p_for->loop);
+ p_for->set_datatype(p_for->loop->get_datatype());
+#ifdef DEBUG_ENABLED
+ if (p_for->variable) {
+ is_shadowing(p_for->variable, R"("for" iterator variable)");
+ }
+#endif
+}
+
+void GDScriptAnalyzer::resolve_while(GDScriptParser::WhileNode *p_while) {
+ resolve_node(p_while->condition);
+
+ resolve_suite(p_while->loop);
+ p_while->set_datatype(p_while->loop->get_datatype());
+}
+
+void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable) {
+ GDScriptParser::DataType type;
+ type.kind = GDScriptParser::DataType::VARIANT; // By default.
+
+ if (p_variable->initializer != nullptr) {
+ reduce_expression(p_variable->initializer);
+ type = p_variable->initializer->get_datatype();
+
+ if (p_variable->infer_datatype) {
+ type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+
+ if (type.has_no_type()) {
+ push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value does not have a set type.)", p_variable->identifier->name), p_variable->initializer);
+ } else if (type.is_variant()) {
+ push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is a variant. Use explicit "Variant" type if this is intended.)", p_variable->identifier->name), p_variable->initializer);
+ } else if (type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
+ push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_variable->identifier->name), p_variable->initializer);
+ }
+ } else {
+ type.type_source = GDScriptParser::DataType::INFERRED;
+ }
+#ifdef DEBUG_ENABLED
+ if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
+ parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name);
+ }
+#endif
+ }
+
+ if (p_variable->datatype_specifier != nullptr) {
+ type = resolve_datatype(p_variable->datatype_specifier);
+ type.is_meta_type = false;
+
+ if (p_variable->initializer != nullptr) {
+ if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) {
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(p_variable->initializer);
+ }
+#ifdef DEBUG_ENABLED
+ } else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+ }
+ if (p_variable->initializer->get_datatype().is_variant()) {
+ // TODO: Warn unsafe assign.
+ mark_node_unsafe(p_variable->initializer);
+ }
+ }
+ } else if (p_variable->infer_datatype) {
+ if (type.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of variable "%s" because the initial value doesn't have a set type.)", p_variable->identifier->name), p_variable->identifier);
+ }
+ type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ }
+
+ type.is_constant = false;
+ p_variable->set_datatype(type);
+
+#ifdef DEBUG_ENABLED
+ if (p_variable->usages == 0 && !String(p_variable->identifier->name).begins_with("_")) {
+ parser->push_warning(p_variable, GDScriptWarning::UNUSED_VARIABLE, p_variable->identifier->name);
+ } else if (p_variable->assignments == 0) {
+ parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
+ }
+
+ is_shadowing(p_variable->identifier, "variable");
+#endif
+}
+
+void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
+ GDScriptParser::DataType type;
+
+ reduce_expression(p_constant->initializer);
+
+ if (!p_constant->initializer->is_constant) {
+ push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
+ }
+
+ type = p_constant->initializer->get_datatype();
+
+#ifdef DEBUG_ENABLED
+ if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
+ parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
+ }
+#endif
+
+ if (p_constant->datatype_specifier != nullptr) {
+ GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier);
+ explicit_type.is_meta_type = false;
+ if (!is_type_compatible(explicit_type, type)) {
+ push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer);
+#ifdef DEBUG_ENABLED
+ } else if (explicit_type.builtin_type == Variant::INT && type.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+ }
+ type = explicit_type;
+ } else if (p_constant->infer_datatype) {
+ if (type.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of constant "%s" because the initial value doesn't have a set type.)", p_constant->identifier->name), p_constant->identifier);
+ }
+ type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ }
+
+ type.is_constant = true;
+ p_constant->set_datatype(type);
+
+#ifdef DEBUG_ENABLED
+ if (p_constant->usages == 0) {
+ parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
+ }
+
+ is_shadowing(p_constant->identifier, "constant");
+#endif
+}
+
+void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
+ reduce_expression(p_assert->condition);
+ if (p_assert->message != nullptr) {
+ reduce_literal(p_assert->message);
+ }
+
+ p_assert->set_datatype(p_assert->condition->get_datatype());
+
+#ifdef DEBUG_ENABLED
+ if (p_assert->condition->is_constant) {
+ if (p_assert->condition->reduced_value.booleanize()) {
+ parser->push_warning(p_assert->condition, GDScriptWarning::ASSERT_ALWAYS_TRUE);
+ } else {
+ parser->push_warning(p_assert->condition, GDScriptWarning::ASSERT_ALWAYS_FALSE);
+ }
+ }
+#endif
+}
+
+void GDScriptAnalyzer::resolve_match(GDScriptParser::MatchNode *p_match) {
+ reduce_expression(p_match->test);
+
+ for (int i = 0; i < p_match->branches.size(); i++) {
+ resolve_match_branch(p_match->branches[i], p_match->test);
+
+ decide_suite_type(p_match, p_match->branches[i]);
+ }
+}
+
+void GDScriptAnalyzer::resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test) {
+ for (int i = 0; i < p_match_branch->patterns.size(); i++) {
+ resolve_match_pattern(p_match_branch->patterns[i], p_match_test);
+ }
+
+ resolve_suite(p_match_branch->block);
+
+ decide_suite_type(p_match_branch, p_match_branch->block);
+}
+
+void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_match_pattern, GDScriptParser::ExpressionNode *p_match_test) {
+ if (p_match_pattern == nullptr) {
+ return;
+ }
+
+ GDScriptParser::DataType result;
+
+ switch (p_match_pattern->pattern_type) {
+ case GDScriptParser::PatternNode::PT_LITERAL:
+ if (p_match_pattern->literal) {
+ reduce_literal(p_match_pattern->literal);
+ result = p_match_pattern->literal->get_datatype();
+ }
+ break;
+ case GDScriptParser::PatternNode::PT_EXPRESSION:
+ if (p_match_pattern->expression) {
+ reduce_expression(p_match_pattern->expression);
+ if (!p_match_pattern->expression->is_constant) {
+ push_error(R"(Expression in match pattern must be a constant.)", p_match_pattern->expression);
+ }
+ result = p_match_pattern->expression->get_datatype();
+ }
+ break;
+ case GDScriptParser::PatternNode::PT_BIND:
+ if (p_match_test != nullptr) {
+ result = p_match_test->get_datatype();
+ } else {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ }
+ p_match_pattern->bind->set_datatype(result);
+#ifdef DEBUG_ENABLED
+ is_shadowing(p_match_pattern->bind, "pattern bind");
+ if (p_match_pattern->bind->usages == 0) {
+ parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNASSIGNED_VARIABLE, p_match_pattern->bind->name);
+ }
+#endif
+ break;
+ case GDScriptParser::PatternNode::PT_ARRAY:
+ for (int i = 0; i < p_match_pattern->array.size(); i++) {
+ resolve_match_pattern(p_match_pattern->array[i], nullptr);
+ decide_suite_type(p_match_pattern, p_match_pattern->array[i]);
+ }
+ result = p_match_pattern->get_datatype();
+ break;
+ case GDScriptParser::PatternNode::PT_DICTIONARY:
+ for (int i = 0; i < p_match_pattern->dictionary.size(); i++) {
+ if (p_match_pattern->dictionary[i].key) {
+ reduce_expression(p_match_pattern->dictionary[i].key);
+ if (!p_match_pattern->dictionary[i].key->is_constant) {
+ push_error(R"(Expression in dictionary pattern key must be a constant.)", p_match_pattern->expression);
+ }
+ }
+
+ if (p_match_pattern->dictionary[i].value_pattern) {
+ resolve_match_pattern(p_match_pattern->dictionary[i].value_pattern, nullptr);
+ decide_suite_type(p_match_pattern, p_match_pattern->dictionary[i].value_pattern);
+ }
+ }
+ result = p_match_pattern->get_datatype();
+ break;
+ case GDScriptParser::PatternNode::PT_WILDCARD:
+ case GDScriptParser::PatternNode::PT_REST:
+ result.kind = GDScriptParser::DataType::VARIANT;
+ break;
+ }
+
+ p_match_pattern->set_datatype(result);
+}
+
+void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
+ GDScriptParser::DataType result;
+ result.kind = GDScriptParser::DataType::VARIANT;
+
+ if (p_parameter->default_value != nullptr) {
+ reduce_expression(p_parameter->default_value);
+ result = p_parameter->default_value->get_datatype();
+ if (p_parameter->infer_datatype) {
+ result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ } else {
+ result.type_source = GDScriptParser::DataType::INFERRED;
+ }
+ result.is_constant = false;
+ }
+
+ if (p_parameter->datatype_specifier != nullptr) {
+ resolve_datatype(p_parameter->datatype_specifier);
+ result = p_parameter->datatype_specifier->get_datatype();
+ result.is_meta_type = false;
+
+ if (p_parameter->default_value != nullptr) {
+ if (!is_type_compatible(result, p_parameter->default_value->get_datatype())) {
+ push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with parameter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value);
+ } else if (p_parameter->default_value->get_datatype().is_variant()) {
+ mark_node_unsafe(p_parameter);
+ }
+ }
+ }
+
+ p_parameter->set_datatype(result);
+}
+
+void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
+ GDScriptParser::DataType result;
+
+ if (p_return->return_value != nullptr) {
+ reduce_expression(p_return->return_value);
+ result = p_return->return_value->get_datatype();
+ } else {
+ // Return type is null by default.
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = Variant::NIL;
+ result.is_constant = true;
+ }
+
+ GDScriptParser::DataType function_type = parser->current_function->get_datatype();
+ function_type.is_meta_type = false;
+ if (function_type.is_hard_type()) {
+ if (!is_type_compatible(function_type, result)) {
+ // Try other way. Okay but not safe.
+ if (!is_type_compatible(result, function_type)) {
+ push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), function_type.to_string()), p_return);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(p_return);
+ }
+#ifdef DEBUG_ENABLED
+ } else if (function_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
+ } else if (result.is_variant()) {
+ mark_node_unsafe(p_return);
+#endif
+ }
+ }
+
+ p_return->set_datatype(result);
+}
+
+void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expression) {
+ // This one makes some magic happen.
+
+ if (p_expression == nullptr) {
+ return;
+ }
+
+ if (p_expression->reduced) {
+ // Don't do this more than once.
+ return;
+ }
+
+ p_expression->reduced = true;
+
+ switch (p_expression->type) {
+ case GDScriptParser::Node::ARRAY:
+ reduce_array(static_cast<GDScriptParser::ArrayNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::ASSIGNMENT:
+ reduce_assignment(static_cast<GDScriptParser::AssignmentNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::AWAIT:
+ reduce_await(static_cast<GDScriptParser::AwaitNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::BINARY_OPERATOR:
+ reduce_binary_op(static_cast<GDScriptParser::BinaryOpNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::CALL:
+ reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::CAST:
+ reduce_cast(static_cast<GDScriptParser::CastNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::DICTIONARY:
+ reduce_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::GET_NODE:
+ reduce_get_node(static_cast<GDScriptParser::GetNodeNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::IDENTIFIER:
+ reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::LITERAL:
+ reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::PRELOAD:
+ reduce_preload(static_cast<GDScriptParser::PreloadNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::SELF:
+ reduce_self(static_cast<GDScriptParser::SelfNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::SUBSCRIPT:
+ reduce_subscript(static_cast<GDScriptParser::SubscriptNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::TERNARY_OPERATOR:
+ reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::UNARY_OPERATOR:
+ reduce_unary_op(static_cast<GDScriptParser::UnaryOpNode *>(p_expression));
+ break;
+ // Non-expressions. Here only to make sure new nodes aren't forgotten.
+ case GDScriptParser::Node::NONE:
+ case GDScriptParser::Node::ANNOTATION:
+ case GDScriptParser::Node::ASSERT:
+ case GDScriptParser::Node::BREAK:
+ case GDScriptParser::Node::BREAKPOINT:
+ case GDScriptParser::Node::CLASS:
+ case GDScriptParser::Node::CONSTANT:
+ case GDScriptParser::Node::CONTINUE:
+ case GDScriptParser::Node::ENUM:
+ case GDScriptParser::Node::FOR:
+ case GDScriptParser::Node::FUNCTION:
+ case GDScriptParser::Node::IF:
+ case GDScriptParser::Node::MATCH:
+ case GDScriptParser::Node::MATCH_BRANCH:
+ case GDScriptParser::Node::PARAMETER:
+ case GDScriptParser::Node::PASS:
+ case GDScriptParser::Node::PATTERN:
+ case GDScriptParser::Node::RETURN:
+ case GDScriptParser::Node::SIGNAL:
+ case GDScriptParser::Node::SUITE:
+ case GDScriptParser::Node::TYPE:
+ case GDScriptParser::Node::VARIABLE:
+ case GDScriptParser::Node::WHILE:
+ ERR_FAIL_MSG("Reaching unreachable case");
+ }
+}
+
+void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
+ bool all_is_constant = true;
+
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ GDScriptParser::ExpressionNode *element = p_array->elements[i];
+ reduce_expression(element);
+ all_is_constant = all_is_constant && element->is_constant;
+ }
+
+ if (all_is_constant) {
+ Array array;
+ array.resize(p_array->elements.size());
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ array[i] = p_array->elements[i]->reduced_value;
+ }
+ p_array->is_constant = true;
+ p_array->reduced_value = array;
+ }
+
+ // It's array in any case.
+ GDScriptParser::DataType arr_type;
+ arr_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ arr_type.kind = GDScriptParser::DataType::BUILTIN;
+ arr_type.builtin_type = Variant::ARRAY;
+ arr_type.is_constant = true;
+
+ p_array->set_datatype(arr_type);
+}
+
+void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) {
+ reduce_expression(p_assignment->assignee);
+ reduce_expression(p_assignment->assigned_value);
+
+ if (p_assignment->assigned_value == nullptr || p_assignment->assignee == nullptr) {
+ return;
+ }
+
+ if (p_assignment->assignee->get_datatype().is_constant) {
+ push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
+ }
+
+ if (!p_assignment->assignee->get_datatype().is_variant() && !p_assignment->assigned_value->get_datatype().is_variant()) {
+ bool compatible = true;
+ GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype();
+ if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value);
+ }
+
+ if (compatible) {
+ compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true);
+ if (!compatible) {
+ if (p_assignment->assignee->get_datatype().is_hard_type()) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(op_type, p_assignment->assignee->get_datatype(), true)) {
+ push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(p_assignment);
+ }
+ } else {
+ // TODO: Warning in this case.
+ mark_node_unsafe(p_assignment);
+ }
+ }
+ } else {
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", p_assignment->assignee->get_datatype().to_string(), p_assignment->assigned_value->get_datatype().to_string()), p_assignment);
+ }
+ }
+
+ if (p_assignment->assignee->get_datatype().has_no_type() || p_assignment->assigned_value->get_datatype().is_variant()) {
+ mark_node_unsafe(p_assignment);
+ }
+
+ if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
+ // Change source type so it's not wrongly detected later.
+ GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_assignment->assignee);
+
+ switch (identifier->source) {
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: {
+ GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
+ if (!id_type.is_hard_type()) {
+ id_type.kind = GDScriptParser::DataType::VARIANT;
+ id_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ identifier->variable_source->set_datatype(id_type);
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
+ GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
+ if (!id_type.is_hard_type()) {
+ id_type = p_assignment->assigned_value->get_datatype();
+ id_type.type_source = GDScriptParser::DataType::INFERRED;
+ id_type.is_constant = false;
+ identifier->variable_source->set_datatype(id_type);
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
+ GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
+ if (!id_type.is_hard_type()) {
+ id_type = p_assignment->assigned_value->get_datatype();
+ id_type.type_source = GDScriptParser::DataType::INFERRED;
+ id_type.is_constant = false;
+ identifier->variable_source->set_datatype(id_type);
+ }
+ } break;
+ default:
+ // Nothing to do.
+ break;
+ }
+ }
+
+ GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
+ GDScriptParser::DataType assigned_type = p_assignment->assigned_value->get_datatype();
+#ifdef DEBUG_ENABLED
+ if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_type.kind == GDScriptParser::DataType::BUILTIN && assigned_type.builtin_type == Variant::NIL) {
+ parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name);
+ } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_type.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
+ }
+#endif
+}
+
+void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) {
+ if (p_await->to_await == nullptr) {
+ GDScriptParser::DataType await_type;
+ await_type.kind = GDScriptParser::DataType::VARIANT;
+ p_await->set_datatype(await_type);
+ return;
+ }
+ if (p_await->to_await->type == GDScriptParser::Node::CALL) {
+ reduce_call(static_cast<GDScriptParser::CallNode *>(p_await->to_await), true);
+ } else {
+ reduce_expression(p_await->to_await);
+ }
+
+ p_await->is_constant = p_await->to_await->is_constant;
+ p_await->reduced_value = p_await->to_await->reduced_value;
+
+ GDScriptParser::DataType awaiting_type = p_await->to_await->get_datatype();
+
+ p_await->set_datatype(awaiting_type);
+
+#ifdef DEBUG_ENABLED
+ if (!awaiting_type.is_coroutine && awaiting_type.builtin_type != Variant::SIGNAL) {
+ parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT);
+ }
+#endif
+}
+
+void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_op) {
+ reduce_expression(p_binary_op->left_operand);
+
+ if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST && p_binary_op->right_operand && p_binary_op->right_operand->type == GDScriptParser::Node::IDENTIFIER) {
+ reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_binary_op->right_operand), true);
+ } else {
+ reduce_expression(p_binary_op->right_operand);
+ }
+ // TODO: Right operand must be a valid type with the `is` operator. Need to check here.
+
+ GDScriptParser::DataType left_type;
+ if (p_binary_op->left_operand) {
+ left_type = p_binary_op->left_operand->get_datatype();
+ }
+ GDScriptParser::DataType right_type;
+ if (p_binary_op->right_operand) {
+ right_type = p_binary_op->right_operand->get_datatype();
+ }
+
+ if (!left_type.is_set() || !right_type.is_set()) {
+ return;
+ }
+
+#ifdef DEBUG_ENABLED
+ if (p_binary_op->variant_op == Variant::OP_DIVIDE && left_type.builtin_type == Variant::INT && right_type.builtin_type == Variant::INT) {
+ parser->push_warning(p_binary_op, GDScriptWarning::INTEGER_DIVISION);
+ }
+#endif
+
+ if (p_binary_op->left_operand->is_constant && p_binary_op->right_operand->is_constant) {
+ p_binary_op->is_constant = true;
+ if (p_binary_op->variant_op < Variant::OP_MAX) {
+ bool valid = false;
+ Variant::evaluate(p_binary_op->variant_op, p_binary_op->left_operand->reduced_value, p_binary_op->right_operand->reduced_value, p_binary_op->reduced_value, valid);
+ if (!valid) {
+ if (p_binary_op->reduced_value.get_type() == Variant::STRING) {
+ push_error(vformat(R"(%s in operator %s.)", p_binary_op->reduced_value, Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
+ } else {
+ push_error(vformat(R"(Invalid operands to operator %s, %s and %s.".)",
+ Variant::get_operator_name(p_binary_op->variant_op),
+ Variant::get_type_name(p_binary_op->left_operand->reduced_value.get_type()),
+ Variant::get_type_name(p_binary_op->right_operand->reduced_value.get_type())),
+ p_binary_op);
+ }
+ }
+ } else {
+ if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) {
+ GDScriptParser::DataType test_type = right_type;
+ test_type.is_meta_type = false;
+
+ if (!is_type_compatible(test_type, p_binary_op->left_operand->get_datatype(), false)) {
+ push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)"), p_binary_op->left_operand);
+ p_binary_op->reduced_value = false;
+ } else {
+ p_binary_op->reduced_value = true;
+ }
+ } else {
+ ERR_PRINT("Parser bug: unknown binary operation.");
+ }
+ }
+ p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value, p_binary_op));
+
+ return;
+ }
+
+ GDScriptParser::DataType result;
+
+ if (left_type.is_variant() || right_type.is_variant()) {
+ // Cannot infer type because one operand can be anything.
+ result.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_binary_op);
+ } else {
+ if (p_binary_op->variant_op < Variant::OP_MAX) {
+ bool valid = false;
+ result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid, p_binary_op);
+
+ if (!valid) {
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
+ }
+ } else {
+ if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) {
+ GDScriptParser::DataType test_type = right_type;
+ test_type.is_meta_type = false;
+
+ if (!is_type_compatible(test_type, p_binary_op->left_operand->get_datatype(), false)) {
+ // Test reverse as well to consider for subtypes.
+ if (!is_type_compatible(p_binary_op->left_operand->get_datatype(), test_type, false)) {
+ if (p_binary_op->left_operand->get_datatype().is_hard_type()) {
+ push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", p_binary_op->left_operand->get_datatype().to_string(), test_type.to_string()), p_binary_op->left_operand);
+ } else {
+ // TODO: Warning.
+ mark_node_unsafe(p_binary_op);
+ }
+ }
+ }
+
+ // "is" operator is always a boolean anyway.
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = Variant::BOOL;
+ } else {
+ ERR_PRINT("Parser bug: unknown binary operation.");
+ }
+ }
+ }
+
+ p_binary_op->set_datatype(result);
+}
+
+void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_await) {
+ bool all_is_constant = true;
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ reduce_expression(p_call->arguments[i]);
+ all_is_constant = all_is_constant && p_call->arguments[i]->is_constant;
+ }
+
+ GDScriptParser::Node::Type callee_type = p_call->get_callee_type();
+ GDScriptParser::DataType call_type;
+
+ if (!p_call->is_super && callee_type == GDScriptParser::Node::IDENTIFIER) {
+ // Call to name directly.
+ StringName function_name = p_call->function_name;
+ Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
+ GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name);
+
+ if (builtin_type < Variant::VARIANT_MAX) {
+ // Is a builtin constructor.
+ call_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ call_type.kind = GDScriptParser::DataType::BUILTIN;
+ call_type.builtin_type = builtin_type;
+
+ if (builtin_type == Variant::OBJECT) {
+ call_type.kind = GDScriptParser::DataType::NATIVE;
+ call_type.native_type = function_name; // "Object".
+ }
+
+ if (all_is_constant) {
+ // Construct here.
+ Vector<const Variant *> args;
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ args.push_back(&(p_call->arguments[i]->reduced_value));
+ }
+
+ Callable::CallError err;
+ Variant value = Variant::construct(builtin_type, (const Variant **)args.ptr(), args.size(), err);
+
+ switch (err.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be %s but is %s.)", Variant::get_type_name(builtin_type), err.argument + 1,
+ Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()),
+ p_call->arguments[err.argument]);
+ break;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD: {
+ String signature = Variant::get_type_name(builtin_type) + "(";
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ if (i > 0) {
+ signature += ", ";
+ }
+ signature += p_call->arguments[i]->get_datatype().to_string();
+ }
+ signature += ")";
+ push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee);
+ } break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ push_error(vformat(R"(Too many arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ break; // Can't happen in a builtin constructor.
+ case Callable::CallError::CALL_OK:
+ p_call->is_constant = true;
+ p_call->reduced_value = value;
+ break;
+ }
+ } else {
+ // TODO: Check constructors without constants.
+
+ // If there's one argument, try to use copy constructor (those aren't explicitly defined).
+ if (p_call->arguments.size() == 1) {
+ GDScriptParser::DataType arg_type = p_call->arguments[0]->get_datatype();
+ if (arg_type.is_variant()) {
+ mark_node_unsafe(p_call->arguments[0]);
+ } else {
+ if (arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == builtin_type) {
+ // Okay.
+ p_call->set_datatype(call_type);
+ return;
+ }
+ }
+ }
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(builtin_type, &constructors);
+ bool match = false;
+
+ for (const List<MethodInfo>::Element *E = constructors.front(); E != nullptr; E = E->next()) {
+ const MethodInfo &info = E->get();
+
+ if (p_call->arguments.size() < info.arguments.size() - info.default_arguments.size()) {
+ continue;
+ }
+ if (p_call->arguments.size() > info.arguments.size()) {
+ continue;
+ }
+
+ bool types_match = true;
+
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ GDScriptParser::DataType par_type = type_from_property(info.arguments[i]);
+
+ if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
+ types_match = false;
+ break;
+#ifdef DEBUG_ENABLED
+ } else {
+ if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
+ }
+#endif
+ }
+ }
+
+ if (types_match) {
+ match = true;
+ call_type = type_from_property(info.return_val);
+ break;
+ }
+ }
+
+ if (!match) {
+ String signature = Variant::get_type_name(builtin_type) + "(";
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ if (i > 0) {
+ signature += ", ";
+ }
+ signature += p_call->arguments[i]->get_datatype().to_string();
+ }
+ signature += ")";
+ push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call);
+ }
+ }
+ p_call->set_datatype(call_type);
+ return;
+ } else if (builtin_function < GDScriptFunctions::FUNC_MAX) {
+ MethodInfo function_info = GDScriptFunctions::get_info(builtin_function);
+
+ if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) {
+ // Can call on compilation.
+ Vector<const Variant *> args;
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ args.push_back(&(p_call->arguments[i]->reduced_value));
+ }
+
+ Variant value;
+ Callable::CallError err;
+ GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err);
+
+ switch (err.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+ PropertyInfo wrong_arg = function_info.arguments[err.argument];
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1,
+ type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ p_call->arguments[err.argument]);
+ } break;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ break; // Can't happen in a builtin constructor.
+ case Callable::CallError::CALL_OK:
+ p_call->is_constant = true;
+ p_call->reduced_value = value;
+ break;
+ }
+ } else {
+ validate_call_arg(function_info, p_call);
+ }
+ p_call->set_datatype(type_from_property(function_info.return_val));
+ return;
+ }
+ }
+
+ GDScriptParser::DataType base_type;
+ call_type.kind = GDScriptParser::DataType::VARIANT;
+ bool is_self = false;
+
+ if (p_call->is_super) {
+ base_type = parser->current_class->base_type;
+ is_self = true;
+ } else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
+ base_type = parser->current_class->get_datatype();
+ is_self = true;
+ } else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
+ GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
+ if (!subscript->is_attribute) {
+ // Invalid call. Error already sent in parser.
+ // TODO: Could check if Callable here.
+ p_call->set_datatype(call_type);
+ mark_node_unsafe(p_call);
+ return;
+ }
+ reduce_expression(subscript->base);
+
+ base_type = subscript->base->get_datatype();
+ } else {
+ // Invalid call. Error already sent in parser.
+ // TODO: Could check if Callable here too.
+ p_call->set_datatype(call_type);
+ mark_node_unsafe(p_call);
+ return;
+ }
+
+ bool is_static = false;
+ bool is_vararg = false;
+ int default_arg_count = 0;
+ GDScriptParser::DataType return_type;
+ List<GDScriptParser::DataType> par_types;
+
+ if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+ validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
+
+ if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
+ push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
+ }
+
+ call_type = return_type;
+ } else {
+ // Check if the name exists as something else.
+ bool found = false;
+ if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) {
+ GDScriptParser::IdentifierNode *callee_id;
+ if (callee_type == GDScriptParser::Node::IDENTIFIER) {
+ callee_id = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee);
+ } else {
+ // Can only be attribute.
+ callee_id = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee)->attribute;
+ }
+ if (callee_id) {
+ reduce_identifier_from_base(callee_id, &base_type);
+ GDScriptParser::DataType callee_datatype = callee_id->get_datatype();
+ if (callee_datatype.is_set() && !callee_datatype.is_variant()) {
+ found = true;
+ if (callee_datatype.builtin_type == Variant::CALLABLE) {
+ push_error(vformat(R"*(Name "%s" is a Callable. You can call it with "%s.call()" instead.)*", p_call->function_name, p_call->function_name), p_call->callee);
+ } else {
+ push_error(vformat(R"*(Name "%s" called as a function but is a "%s".)*", p_call->function_name, callee_datatype.to_string()), p_call->callee);
+ }
+#ifdef DEBUG_ENABLED
+ } else if (!is_self) {
+ parser->push_warning(p_call, GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->function_name, base_type.to_string());
+ mark_node_unsafe(p_call);
+#endif
+ }
+ }
+ }
+ if (!found && is_self) {
+ String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
+ push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
+ }
+ }
+
+ if (call_type.is_coroutine && !is_await) {
+ push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call->callee);
+ }
+
+ p_call->set_datatype(call_type);
+}
+
+void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
+ reduce_expression(p_cast->operand);
+
+ GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
+
+ if (!cast_type.is_set()) {
+ return;
+ }
+
+ cast_type.is_meta_type = false; // The casted value won't be a type name.
+ p_cast->set_datatype(cast_type);
+
+ if (!cast_type.is_variant()) {
+ GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
+ if (!op_type.is_variant()) {
+ bool valid = false;
+ if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
+ valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
+ } else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
+ valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
+ }
+
+ if (!valid) {
+ push_error(vformat(R"(Invalid cast. Cannot convert from "%s" to "%s".)", op_type.to_string(), cast_type.to_string()), p_cast->cast_type);
+ }
+ }
+ } else {
+ mark_node_unsafe(p_cast);
+ }
+#ifdef DEBUG_ENABLED
+ if (p_cast->operand->get_datatype().is_variant()) {
+ parser->push_warning(p_cast, GDScriptWarning::UNSAFE_CAST, cast_type.to_string());
+ mark_node_unsafe(p_cast);
+ }
+#endif
+
+ // TODO: Perform cast on constants.
+}
+
+void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
+ bool all_is_constant = true;
+
+ HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements;
+
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ if (p_dictionary->style == GDScriptParser::DictionaryNode::PYTHON_DICT) {
+ reduce_expression(element.key);
+ }
+ reduce_expression(element.value);
+ all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
+
+ if (element.key->is_constant) {
+ if (elements.has(element.key->reduced_value)) {
+ push_error(vformat(R"(Key "%s" was already used in this dictionary (at line %d).)", element.key->reduced_value, elements[element.key->reduced_value]->start_line), element.key);
+ } else {
+ elements[element.key->reduced_value] = element.value;
+ }
+ }
+ }
+
+ if (all_is_constant) {
+ Dictionary dict;
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ dict[element.key->reduced_value] = element.value->reduced_value;
+ }
+ p_dictionary->is_constant = true;
+ p_dictionary->reduced_value = dict;
+ }
+
+ // It's dictionary in any case.
+ GDScriptParser::DataType dict_type;
+ dict_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ dict_type.kind = GDScriptParser::DataType::BUILTIN;
+ dict_type.builtin_type = Variant::DICTIONARY;
+ dict_type.is_constant = true;
+
+ p_dictionary->set_datatype(dict_type);
+}
+
+void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) {
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.native_type = "Node";
+ result.builtin_type = Variant::OBJECT;
+
+ if (!ClassDB::is_parent_class(get_real_class_name(parser->current_class->base_type.native_type), result.native_type)) {
+ push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
+ }
+
+ p_get_node->set_datatype(result);
+}
+
+GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name) {
+ Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
+ ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::CLASS;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
+ type.class_type = ref->get_parser()->head;
+ type.script_path = ref->get_parser()->script_path;
+ type.is_constant = true;
+ type.is_meta_type = true;
+ return type;
+}
+
+void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) {
+ GDScriptParser::DataType base;
+ if (p_base == nullptr) {
+ base = type_from_metatype(parser->current_class->get_datatype());
+ } else {
+ base = *p_base;
+ }
+
+ const StringName &name = p_identifier->name;
+
+ if (base.kind == GDScriptParser::DataType::BUILTIN) {
+ if (base.is_meta_type) {
+ bool valid = true;
+ Variant result = Variant::get_constant_value(base.builtin_type, name, &valid);
+ if (valid) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = result;
+ p_identifier->set_datatype(type_from_variant(result, p_identifier));
+ } else {
+ push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier);
+ }
+ } else {
+ switch (base.builtin_type) {
+ case Variant::NIL: {
+ push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier);
+ return;
+ }
+ case Variant::DICTIONARY: {
+ GDScriptParser::DataType dummy;
+ dummy.kind = GDScriptParser::DataType::VARIANT;
+ p_identifier->set_datatype(dummy);
+ return;
+ }
+ default: {
+ Callable::CallError temp;
+ Variant dummy = Variant::construct(base.builtin_type, nullptr, 0, temp);
+ List<PropertyInfo> properties;
+ dummy.get_property_list(&properties);
+ for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) {
+ const PropertyInfo &prop = E->get();
+ if (prop.name == name) {
+ p_identifier->set_datatype(type_from_property(prop));
+ return;
+ }
+ }
+ push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
+ }
+ }
+ }
+ return;
+ }
+
+ if (base.kind == GDScriptParser::DataType::ENUM) {
+ if (base.is_meta_type) {
+ if (base.enum_values.has(name)) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = base.enum_values[name];
+
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::ENUM_VALUE;
+ result.native_type = base.native_type;
+ result.enum_type = name;
+ p_identifier->set_datatype(result);
+ } else {
+ push_error(vformat(R"(Cannot find value "%s" in "%s".)", name, base.to_string()), p_identifier);
+ }
+ } else {
+ push_error(R"(Cannot get property from enum value.)", p_identifier);
+ }
+ return;
+ }
+
+ GDScriptParser::ClassNode *base_class = base.class_type;
+
+ // TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
+ while (base_class != nullptr) {
+ if (base_class->identifier && base_class->identifier->name == name) {
+ p_identifier->set_datatype(base_class->get_datatype());
+ return;
+ }
+ if (base_class->has_member(name)) {
+ const GDScriptParser::ClassNode::Member &member = base_class->get_member(name);
+ p_identifier->set_datatype(member.get_datatype());
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::CONSTANT:
+ // For out-of-order resolution:
+ reduce_expression(member.constant->initializer);
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.constant->initializer->reduced_value;
+ p_identifier->set_datatype(member.constant->initializer->get_datatype());
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
+ p_identifier->constant_source = member.constant;
+ break;
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE:
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.enum_value.value;
+ break;
+ case GDScriptParser::ClassNode::Member::VARIABLE:
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
+ p_identifier->variable_source = member.variable;
+ break;
+ case GDScriptParser::ClassNode::Member::FUNCTION:
+ resolve_function_signature(member.function);
+ p_identifier->set_datatype(make_callable_type(member.function->info));
+ break;
+ default:
+ break; // Type already set.
+ }
+ return;
+ }
+ // Check outer constants.
+ // TODO: Allow outer static functions.
+ GDScriptParser::ClassNode *outer = base_class->outer;
+ while (outer != nullptr) {
+ if (outer->has_member(name)) {
+ const GDScriptParser::ClassNode::Member &member = outer->get_member(name);
+ if (member.type == GDScriptParser::ClassNode::Member::CONSTANT) {
+ // TODO: Make sure loops won't cause problem. And make special error message for those.
+ // For out-of-order resolution:
+ reduce_expression(member.constant->initializer);
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.constant->initializer->reduced_value;
+ return;
+ }
+ }
+ outer = outer->outer;
+ }
+
+ base_class = base_class->base_type.class_type;
+ }
+
+ // Check native members.
+ const StringName &native = get_real_class_name(base.native_type);
+
+ if (class_exists(native)) {
+ PropertyInfo prop_info;
+ MethodInfo method_info;
+ if (ClassDB::get_property_info(native, name, &prop_info)) {
+ p_identifier->set_datatype(type_from_property(prop_info));
+ return;
+ }
+ if (ClassDB::get_method_info(native, name, &method_info)) {
+ // Method is callable.
+ p_identifier->set_datatype(make_callable_type(method_info));
+ return;
+ }
+ if (ClassDB::get_signal(native, name, &method_info)) {
+ // Signal is a type too.
+ p_identifier->set_datatype(make_signal_type(method_info));
+ return;
+ }
+ if (ClassDB::has_enum(native, name)) {
+ p_identifier->set_datatype(make_native_enum_type(native, name));
+ return;
+ }
+ bool valid = false;
+ int int_constant = ClassDB::get_integer_constant(native, name, &valid);
+ if (valid) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = int_constant;
+ p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
+ return;
+ }
+ }
+}
+
+void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin) {
+ // TODO: This is opportunity to further infer types.
+
+ // Check if we are inside and enum. This allows enum values to access other elements of the same enum.
+ if (current_enum) {
+ for (int i = 0; i < current_enum->values.size(); i++) {
+ const GDScriptParser::EnumNode::Value &element = current_enum->values[i];
+ if (element.identifier->name == p_identifier->name) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN;
+ type.builtin_type = Variant::INT;
+ type.is_constant = true;
+ if (element.parent_enum->identifier) {
+ type.enum_type = element.parent_enum->identifier->name;
+ }
+ p_identifier->set_datatype(type);
+
+ if (element.resolved) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = element.value;
+ } else {
+ push_error(R"(Cannot use another enum element before it was declared.)", p_identifier);
+ }
+ return; // Found anyway.
+ }
+ }
+ }
+
+ // Check if identifier is local.
+ // If that's the case, the declaration already was solved before.
+ switch (p_identifier->source) {
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
+ p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
+ return;
+ case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
+ case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+ p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
+ p_identifier->is_constant = true;
+ // TODO: Constant should have a value on the node itself.
+ p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
+ return;
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ p_identifier->variable_source->usages++;
+ [[fallthrough]];
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
+ p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
+ return;
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
+ p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
+ return;
+ case GDScriptParser::IdentifierNode::LOCAL_BIND: {
+ GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
+ result.is_constant = true;
+ p_identifier->set_datatype(result);
+ return;
+ }
+ case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
+ break;
+ }
+
+ // Not a local, so check members.
+ reduce_identifier_from_base(p_identifier);
+ if (p_identifier->get_datatype().is_set()) {
+ // Found.
+ return;
+ }
+
+ StringName name = p_identifier->name;
+ p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
+
+ // Check globals.
+ if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) {
+ if (can_be_builtin) {
+ p_identifier->set_datatype(make_builtin_meta_type(GDScriptParser::get_builtin_type(name)));
+ return;
+ } else {
+ push_error(R"(Builtin type cannot be used as a name on its own.)", p_identifier);
+ }
+ }
+
+ if (class_exists(name)) {
+ p_identifier->set_datatype(make_native_meta_type(name));
+ return;
+ }
+
+ if (ScriptServer::is_global_class(name)) {
+ p_identifier->set_datatype(make_global_class_meta_type(name));
+ return;
+ }
+
+ // Try singletons.
+ // Do this before globals because this might be a singleton loading another one before it's compiled.
+ if (ProjectSettings::get_singleton()->has_autoload(name)) {
+ const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(name);
+ if (autoload.is_singleton) {
+ // Singleton exists, so it's at least a Node.
+ GDScriptParser::DataType result;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ if (autoload.path.to_lower().ends_with(GDScriptLanguage::get_singleton()->get_extension())) {
+ Ref<GDScriptParserRef> parser = get_parser_for(autoload.path);
+ if (parser.is_valid()) {
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err == OK) {
+ result = type_from_metatype(parser->get_parser()->head->get_datatype());
+ }
+ }
+ }
+ result.is_constant = true;
+ p_identifier->set_datatype(result);
+ return;
+ }
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) {
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[name];
+ Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ p_identifier->set_datatype(type_from_variant(constant, p_identifier));
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = constant;
+ return;
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) {
+ Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name];
+ p_identifier->set_datatype(type_from_variant(constant, p_identifier));
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = constant;
+ return;
+ }
+
+ // Not found.
+ // Check if it's a builtin function.
+ if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) {
+ push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
+ } else {
+ push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
+ }
+ GDScriptParser::DataType dummy;
+ dummy.kind = GDScriptParser::DataType::VARIANT;
+ p_identifier->set_datatype(dummy); // Just so type is set to something.
+}
+
+void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
+ p_literal->reduced_value = p_literal->value;
+ p_literal->is_constant = true;
+
+ p_literal->set_datatype(type_from_variant(p_literal->reduced_value, p_literal));
+}
+
+void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
+ if (!p_preload->path) {
+ return;
+ }
+
+ reduce_expression(p_preload->path);
+
+ if (!p_preload->path->is_constant) {
+ push_error("Preloaded path must be a constant string.", p_preload->path);
+ return;
+ }
+
+ if (p_preload->path->reduced_value.get_type() != Variant::STRING) {
+ push_error("Preloaded path must be a constant string.", p_preload->path);
+ } else {
+ p_preload->resolved_path = p_preload->path->reduced_value;
+ // TODO: Save this as script dependency.
+ if (p_preload->resolved_path.is_rel_path()) {
+ p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path);
+ }
+ p_preload->resolved_path = p_preload->resolved_path.simplify_path();
+ if (!FileAccess::exists(p_preload->resolved_path)) {
+ push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path);
+ } else {
+ // TODO: Don't load if validating: use completion cache.
+ p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
+ if (p_preload->resource.is_null()) {
+ push_error(vformat(R"(Could not p_preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
+ }
+ }
+ }
+
+ p_preload->is_constant = true;
+ p_preload->reduced_value = p_preload->resource;
+ p_preload->set_datatype(type_from_variant(p_preload->reduced_value, p_preload));
+}
+
+void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
+ p_self->is_constant = false;
+ p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype()));
+}
+
+void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
+ if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
+ reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true);
+ } else {
+ reduce_expression(p_subscript->base);
+ }
+
+ GDScriptParser::DataType result_type;
+
+ // Reduce index first. If it's a constant StringName, use attribute instead.
+ if (!p_subscript->is_attribute) {
+ if (p_subscript->index == nullptr) {
+ return;
+ }
+ reduce_expression(p_subscript->index);
+
+ if (p_subscript->index->is_constant && p_subscript->index->reduced_value.get_type() == Variant::STRING_NAME) {
+ GDScriptParser::IdentifierNode *attribute = parser->alloc_node<GDScriptParser::IdentifierNode>();
+ // Copy location for better error message.
+ attribute->start_line = p_subscript->index->start_line;
+ attribute->end_line = p_subscript->index->end_line;
+ attribute->leftmost_column = p_subscript->index->leftmost_column;
+ attribute->rightmost_column = p_subscript->index->rightmost_column;
+ p_subscript->is_attribute = true;
+ p_subscript->attribute = attribute;
+ }
+ }
+
+ if (p_subscript->is_attribute) {
+ if (p_subscript->attribute == nullptr) {
+ return;
+ }
+ if (p_subscript->base->is_constant) {
+ // Just try to get it.
+ bool valid = false;
+ Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, &valid);
+ if (!valid) {
+ push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
+ } else {
+ p_subscript->is_constant = true;
+ p_subscript->reduced_value = value;
+ result_type = type_from_variant(value, p_subscript);
+ }
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
+
+ if (base_type.is_variant()) {
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_subscript);
+ } else {
+ reduce_identifier_from_base(p_subscript->attribute, &base_type);
+ GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
+ if (attr_type.is_set()) {
+ result_type = attr_type;
+ p_subscript->is_constant = p_subscript->attribute->is_constant;
+ p_subscript->reduced_value = p_subscript->attribute->reduced_value;
+ } else {
+ if (base_type.kind == GDScriptParser::DataType::BUILTIN) {
+ push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, base_type.to_string()), p_subscript->attribute);
+#ifdef DEBUG_ENABLED
+ } else {
+ parser->push_warning(p_subscript, GDScriptWarning::UNSAFE_PROPERTY_ACCESS, p_subscript->attribute->name, base_type.to_string());
+#endif
+ }
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ }
+ }
+ }
+ } else {
+ // Index was already reduced before.
+
+ if (p_subscript->base->is_constant && p_subscript->index->is_constant) {
+ // Just try to get it.
+ bool valid = false;
+ Variant value = p_subscript->base->reduced_value.get(p_subscript->index->reduced_value, &valid);
+ if (!valid) {
+ push_error(vformat(R"(Cannot get index "%s" from "%s".)", p_subscript->index->reduced_value, p_subscript->base->reduced_value), p_subscript->index);
+ } else {
+ p_subscript->is_constant = true;
+ p_subscript->reduced_value = value;
+ result_type = type_from_variant(value, p_subscript);
+ }
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
+ GDScriptParser::DataType index_type = p_subscript->index->get_datatype();
+
+ if (base_type.is_variant()) {
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_subscript);
+ } else {
+ if (base_type.kind == GDScriptParser::DataType::BUILTIN && !index_type.is_variant()) {
+ // Check if indexing is valid.
+ bool error = index_type.kind != GDScriptParser::DataType::BUILTIN && base_type.builtin_type != Variant::DICTIONARY;
+ if (!error) {
+ switch (base_type.builtin_type) {
+ // Expect int or real as index.
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::ARRAY:
+ case Variant::STRING:
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT;
+ break;
+ // Expect String only.
+ case Variant::RECT2:
+ case Variant::RECT2I:
+ case Variant::PLANE:
+ case Variant::QUAT:
+ case Variant::AABB:
+ case Variant::OBJECT:
+ error = index_type.builtin_type != Variant::STRING;
+ break;
+ // Expect String or number.
+ case Variant::BASIS:
+ case Variant::VECTOR2:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3:
+ case Variant::VECTOR3I:
+ case Variant::TRANSFORM:
+ case Variant::TRANSFORM2D:
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT &&
+ index_type.builtin_type != Variant::STRING;
+ break;
+ // Expect String or int.
+ case Variant::COLOR:
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
+ break;
+ // Don't support indexing, but we will check it later.
+ case Variant::_RID:
+ case Variant::BOOL:
+ case Variant::CALLABLE:
+ case Variant::FLOAT:
+ case Variant::INT:
+ case Variant::NIL:
+ case Variant::NODE_PATH:
+ case Variant::SIGNAL:
+ case Variant::STRING_NAME:
+ break;
+ // Here for completeness.
+ case Variant::DICTIONARY:
+ case Variant::VARIANT_MAX:
+ break;
+ }
+
+ if (error) {
+ push_error(vformat(R"(Invalid index type "%s" for a base of type "%s".)", index_type.to_string(), base_type.to_string()), p_subscript->index);
+ }
+ }
+ } else if (base_type.kind != GDScriptParser::DataType::BUILTIN && !index_type.is_variant()) {
+ if (index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME) {
+ push_error(vformat(R"(Only String or StringName can be used as index for type "%s", but received a "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index);
+ }
+ }
+
+ // Check resulting type if possible.
+ result_type.builtin_type = Variant::NIL;
+ result_type.kind = GDScriptParser::DataType::BUILTIN;
+ result_type.type_source = base_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
+
+ switch (base_type.builtin_type) {
+ // Can't index at all.
+ case Variant::_RID:
+ case Variant::BOOL:
+ case Variant::CALLABLE:
+ case Variant::FLOAT:
+ case Variant::INT:
+ case Variant::NIL:
+ case Variant::NODE_PATH:
+ case Variant::SIGNAL:
+ case Variant::STRING_NAME:
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ push_error(vformat(R"(Cannot use subscript operator on a base of type "%s".)", base_type.to_string()), p_subscript->base);
+ break;
+ // Return int.
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3I:
+ result_type.builtin_type = Variant::INT;
+ break;
+ // Return float.
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::QUAT:
+ result_type.builtin_type = Variant::FLOAT;
+ break;
+ // Return Color.
+ case Variant::PACKED_COLOR_ARRAY:
+ result_type.builtin_type = Variant::COLOR;
+ break;
+ // Return String.
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::STRING:
+ result_type.builtin_type = Variant::STRING;
+ break;
+ // Return Vector2.
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::TRANSFORM2D:
+ case Variant::RECT2:
+ result_type.builtin_type = Variant::VECTOR2;
+ break;
+ // Return Vector2I.
+ case Variant::RECT2I:
+ result_type.builtin_type = Variant::VECTOR2I;
+ break;
+ // Return Vector3.
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::AABB:
+ case Variant::BASIS:
+ result_type.builtin_type = Variant::VECTOR3;
+ break;
+ // Depends on the index.
+ case Variant::TRANSFORM:
+ case Variant::PLANE:
+ case Variant::COLOR:
+ case Variant::ARRAY:
+ case Variant::DICTIONARY:
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ result_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ break;
+ // Here for completeness.
+ case Variant::OBJECT:
+ case Variant::VARIANT_MAX:
+ break;
+ }
+ }
+ }
+ }
+
+ p_subscript->set_datatype(result_type);
+}
+
+void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op) {
+ reduce_expression(p_ternary_op->condition);
+ reduce_expression(p_ternary_op->true_expr);
+ reduce_expression(p_ternary_op->false_expr);
+
+ GDScriptParser::DataType result;
+
+ if (p_ternary_op->condition && p_ternary_op->condition->is_constant && p_ternary_op->true_expr->is_constant && p_ternary_op->false_expr && p_ternary_op->false_expr->is_constant) {
+ p_ternary_op->is_constant = true;
+ if (p_ternary_op->condition->reduced_value.booleanize()) {
+ p_ternary_op->reduced_value = p_ternary_op->true_expr->reduced_value;
+ } else {
+ p_ternary_op->reduced_value = p_ternary_op->false_expr->reduced_value;
+ }
+ }
+
+ GDScriptParser::DataType true_type;
+ if (p_ternary_op->true_expr) {
+ true_type = p_ternary_op->true_expr->get_datatype();
+ } else {
+ true_type.kind = GDScriptParser::DataType::VARIANT;
+ }
+ GDScriptParser::DataType false_type;
+ if (p_ternary_op->false_expr) {
+ false_type = p_ternary_op->false_expr->get_datatype();
+ } else {
+ false_type.kind = GDScriptParser::DataType::VARIANT;
+ }
+
+ if (true_type.is_variant() || false_type.is_variant()) {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ result = true_type;
+ if (!is_type_compatible(true_type, false_type)) {
+ result = false_type;
+ if (!is_type_compatible(false_type, true_type)) {
+ result.type_source = GDScriptParser::DataType::UNDETECTED;
+ result.kind = GDScriptParser::DataType::VARIANT;
+#ifdef DEBUG_ENABLED
+ parser->push_warning(p_ternary_op, GDScriptWarning::INCOMPATIBLE_TERNARY);
+#endif
+ }
+ }
+ }
+
+ p_ternary_op->set_datatype(result);
+}
+
+void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) {
+ reduce_expression(p_unary_op->operand);
+
+ GDScriptParser::DataType result;
+
+ if (p_unary_op->operand == nullptr) {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ p_unary_op->set_datatype(result);
+ return;
+ }
+
+ if (p_unary_op->operand->is_constant) {
+ p_unary_op->is_constant = true;
+ p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant());
+ result = type_from_variant(p_unary_op->reduced_value, p_unary_op);
+ } else if (p_unary_op->operand->get_datatype().is_variant()) {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_unary_op);
+ } else {
+ bool valid = false;
+ result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op);
+
+ if (!valid) {
+ push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
+ }
+ }
+
+ p_unary_op->set_datatype(result);
+}
+
+GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
+ GDScriptParser::DataType result;
+ result.is_constant = true;
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = p_value.get_type();
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; // Constant has explicit type.
+
+ if (p_value.get_type() == Variant::OBJECT) {
+ Object *obj = p_value;
+ if (!obj) {
+ return GDScriptParser::DataType();
+ }
+ result.native_type = obj->get_class_name();
+
+ Ref<Script> scr = p_value; // Check if value is a script itself.
+ if (scr.is_valid()) {
+ result.is_meta_type = true;
+ } else {
+ result.is_meta_type = false;
+ scr = obj->get_script();
+ }
+ if (scr.is_valid()) {
+ if (scr->is_valid()) {
+ result.script_type = scr;
+ result.script_path = scr->get_path();
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ result.kind = GDScriptParser::DataType::CLASS;
+ // This might be an inner class, so we want to get the parser for the root.
+ // But still get the inner class from that tree.
+ GDScript *current = gds.ptr();
+ List<StringName> class_chain;
+ while (current->_owner) {
+ // Push to front so it's in reverse.
+ class_chain.push_front(current->name);
+ current = current->_owner;
+ }
+
+ Ref<GDScriptParserRef> ref = get_parser_for(current->path);
+ ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+
+ GDScriptParser::ClassNode *found = ref->get_parser()->head;
+
+ // It should be okay to assume this exists, since we have a complete script already.
+ for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
+ found = found->get_member(E->get()).m_class;
+ }
+
+ result.class_type = found;
+ result.script_path = ref->get_parser()->script_path;
+ } else {
+ result.kind = GDScriptParser::DataType::SCRIPT;
+ }
+ result.native_type = scr->get_instance_base_type();
+ } else {
+ push_error(vformat(R"(Constant value uses script from "%s" which is loaded but not compiled.)", scr->get_path()), p_source);
+ result.kind = GDScriptParser::DataType::VARIANT;
+ result.type_source = GDScriptParser::DataType::UNDETECTED;
+ result.is_meta_type = false;
+ }
+ } else {
+ result.kind = GDScriptParser::DataType::NATIVE;
+ if (result.native_type == GDScriptNativeClass::get_class_static()) {
+ result.is_meta_type = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) const {
+ GDScriptParser::DataType result = p_meta_type;
+ result.is_meta_type = false;
+ result.is_constant = false;
+ return result;
+}
+
+GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property) const {
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ if (p_property.type == Variant::NIL && (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ // Variant
+ result.kind = GDScriptParser::DataType::VARIANT;
+ return result;
+ }
+ result.builtin_type = p_property.type;
+ if (p_property.type == Variant::OBJECT) {
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
+ } else {
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ }
+ return result;
+}
+
+bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) {
+ r_static = false;
+ r_vararg = false;
+ r_default_arg_count = 0;
+ StringName function_name = p_function;
+
+ if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
+ // Construct a base type to get methods.
+ Callable::CallError err;
+ Variant dummy = Variant::construct(p_base_type.builtin_type, nullptr, 0, err);
+ if (err.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(false, "Could not construct base Variant type.");
+ }
+ List<MethodInfo> methods;
+ dummy.get_method_list(&methods);
+
+ for (const List<MethodInfo>::Element *E = methods.front(); E != nullptr; E = E->next()) {
+ if (E->get().name == p_function) {
+ return function_signature_from_info(E->get(), r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ }
+ }
+
+ return false;
+ }
+
+ bool is_constructor = p_base_type.is_meta_type && p_function == "new";
+ if (is_constructor) {
+ function_name = "_init";
+ r_static = true;
+ }
+
+ GDScriptParser::ClassNode *base_class = p_base_type.class_type;
+ GDScriptParser::FunctionNode *found_function = nullptr;
+
+ while (found_function == nullptr && base_class != nullptr) {
+ if (base_class->has_member(function_name)) {
+ if (base_class->get_member(function_name).type != GDScriptParser::ClassNode::Member::FUNCTION) {
+ // TODO: If this is Callable it can have a better error message.
+ push_error(vformat(R"(Member "%s" is not a function.)", function_name), p_source);
+ return false;
+ }
+ found_function = base_class->get_member(function_name).function;
+ }
+ base_class = base_class->base_type.class_type;
+ }
+
+ if (found_function != nullptr) {
+ r_static = is_constructor || found_function->is_static;
+ for (int i = 0; i < found_function->parameters.size(); i++) {
+ r_par_types.push_back(found_function->parameters[i]->get_datatype());
+ if (found_function->parameters[i]->default_value != nullptr) {
+ r_default_arg_count++;
+ }
+ }
+ r_return_type = found_function->get_datatype();
+ r_return_type.is_coroutine = found_function->is_coroutine;
+
+ return true;
+ }
+
+ Ref<Script> base_script = p_base_type.script_type;
+
+ while (base_script.is_valid() && base_script->is_valid()) {
+ MethodInfo info = base_script->get_method_info(function_name);
+
+ if (!(info == MethodInfo())) {
+ return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ }
+ base_script = base_script->get_base_script();
+ }
+
+ // If the base is a script, it might be trying to access members of the Script class itself.
+ if (p_base_type.is_meta_type && !is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) {
+ MethodInfo info;
+ StringName script_class = p_base_type.kind == GDScriptParser::DataType::SCRIPT ? p_base_type.script_type->get_class_name() : StringName(GDScript::get_class_static());
+
+ if (ClassDB::get_method_info(script_class, function_name, &info)) {
+ return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ }
+ }
+
+ StringName base_native = p_base_type.native_type;
+#ifdef DEBUG_ENABLED
+ if (base_native != StringName()) {
+ // Empty native class might happen in some Script implementations.
+ // Just ignore it.
+ if (!class_exists(base_native)) {
+ ERR_FAIL_V_MSG(false, vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native));
+ }
+ }
+#endif
+
+ if (is_constructor) {
+ // Native types always have a default constructor.
+ r_return_type = p_base_type;
+ r_return_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_return_type.is_meta_type = false;
+ return true;
+ }
+
+ StringName real_native = get_real_class_name(base_native);
+
+ MethodInfo info;
+ if (ClassDB::get_method_info(real_native, function_name, &info)) {
+ return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ }
+
+ return false;
+}
+
+bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) {
+ r_return_type = type_from_property(p_info.return_val);
+ r_default_arg_count = p_info.default_arguments.size();
+ r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0;
+
+ for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E != nullptr; E = E->next()) {
+ r_par_types.push_back(type_from_property(E->get()));
+ }
+ return true;
+}
+
+bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call) {
+ List<GDScriptParser::DataType> arg_types;
+
+ for (const List<PropertyInfo>::Element *E = p_method.arguments.front(); E != nullptr; E = E->next()) {
+ arg_types.push_back(type_from_property(E->get()));
+ }
+
+ return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
+}
+
+bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call) {
+ bool valid = true;
+
+ if (p_call->arguments.size() < p_par_types.size() - p_default_args_count) {
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", p_call->function_name, p_par_types.size() - p_default_args_count, p_call->arguments.size()), p_call);
+ valid = false;
+ }
+ if (!p_is_vararg && p_call->arguments.size() > p_par_types.size()) {
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", p_call->function_name, p_par_types.size(), p_call->arguments.size()), p_call->arguments[p_par_types.size()]);
+ valid = false;
+ }
+
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ if (i >= p_par_types.size()) {
+ // Already on vararg place.
+ break;
+ }
+ GDScriptParser::DataType par_type = p_par_types[i];
+ GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
+
+ if (arg_type.is_variant()) {
+ // Argument can be anything, so this is unsafe.
+ mark_node_unsafe(p_call->arguments[i]);
+ } else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) {
+ // Supertypes are acceptable for dynamic compliance, but it's unsafe.
+ mark_node_unsafe(p_call);
+ if (!is_type_compatible(arg_type, par_type)) {
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*",
+ p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
+ p_call->arguments[i]);
+ valid = false;
+ }
+#ifdef DEBUG_ENABLED
+ } else {
+ if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
+ }
+#endif
+ }
+ }
+ return valid;
+}
+
+#ifdef DEBUG_ENABLED
+bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context) {
+ const StringName &name = p_local->name;
+ GDScriptParser::DataType base = parser->current_class->get_datatype();
+
+ GDScriptParser::ClassNode *base_class = base.class_type;
+
+ while (base_class != nullptr) {
+ if (base_class->has_member(name)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_local->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
+ return true;
+ }
+ base_class = base_class->base_type.class_type;
+ }
+
+ StringName base_native = base.native_type;
+
+ ERR_FAIL_COND_V_MSG(!class_exists(base_native), false, "Non-existent native base class.");
+
+ StringName parent = base_native;
+ while (parent != StringName()) {
+ StringName real_class_name = get_real_class_name(parent);
+ if (ClassDB::has_method(real_class_name, name, true)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent);
+ return true;
+ } else if (ClassDB::has_signal(real_class_name, name, true)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "signal", parent);
+ return true;
+ } else if (ClassDB::has_property(real_class_name, name, true)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "property", parent);
+ return true;
+ } else if (ClassDB::has_integer_constant(real_class_name, name, true)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "constant", parent);
+ return true;
+ } else if (ClassDB::has_enum(real_class_name, name, true)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "enum", parent);
+ return true;
+ }
+ parent = ClassDB::get_parent_class(real_class_name);
+ }
+
+ return false;
+}
+#endif
+
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
+ // This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations.
+
+ GDScriptParser::DataType result;
+ result.kind = GDScriptParser::DataType::VARIANT;
+
+ Variant::Type a_type = p_a.builtin_type;
+ Variant::Type b_type = p_b.builtin_type;
+
+ Variant a;
+ REF a_ref;
+ if (a_type == Variant::OBJECT) {
+ a_ref.instance();
+ a = a_ref;
+ } else {
+ Callable::CallError err;
+ a = Variant::construct(a_type, nullptr, 0, err);
+ if (err.error != Callable::CallError::CALL_OK) {
+ r_valid = false;
+ ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(a_type)));
+ }
+ }
+ Variant b;
+ REF b_ref;
+ if (b_type == Variant::OBJECT) {
+ b_ref.instance();
+ b = b_ref;
+ } else {
+ Callable::CallError err;
+ b = Variant::construct(b_type, nullptr, 0, err);
+ if (err.error != Callable::CallError::CALL_OK) {
+ r_valid = false;
+ ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(b_type)));
+ }
+ }
+
+ // Avoid division by zero.
+ switch (b_type) {
+ case Variant::INT:
+ b = 1;
+ break;
+ case Variant::FLOAT:
+ b = 1.0;
+ break;
+ case Variant::VECTOR2:
+ b = Vector2(1.0, 1.0);
+ break;
+ case Variant::VECTOR2I:
+ b = Vector2i(1, 1);
+ break;
+ case Variant::VECTOR3:
+ b = Vector3(1.0, 1.0, 1.0);
+ break;
+ case Variant::VECTOR3I:
+ b = Vector3i(1, 1, 1);
+ break;
+ case Variant::COLOR:
+ b = Color(1.0, 1.0, 1.0, 1.0);
+ break;
+ default:
+ // No change needed.
+ break;
+ }
+
+ // Avoid error in formatting operator (%) where it doesn't find a placeholder.
+ if (a_type == Variant::STRING && b_type != Variant::ARRAY) {
+ a = String("%s");
+ }
+
+ Variant ret;
+ Variant::evaluate(p_operation, a, b, ret, r_valid);
+
+ if (r_valid) {
+ return type_from_variant(ret, p_source);
+ }
+
+ return result;
+}
+
+// TODO: Add safe/unsafe return variable (for variant cases)
+bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion) const {
+ // These return "true" so it doesn't affect users negatively.
+ ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type");
+ ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type");
+
+ if (p_target.kind == GDScriptParser::DataType::VARIANT) {
+ // Variant can receive anything.
+ return true;
+ }
+
+ if (p_source.kind == GDScriptParser::DataType::VARIANT) {
+ // TODO: This is acceptable but unsafe. Make sure unsafe line is set.
+ return true;
+ }
+
+ if (p_target.kind == GDScriptParser::DataType::BUILTIN) {
+ bool valid = p_source.kind == GDScriptParser::DataType::BUILTIN && p_target.builtin_type == p_source.builtin_type;
+ if (!valid && p_allow_implicit_conversion) {
+ valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type);
+ }
+ if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ // Enum value is also integer.
+ valid = true;
+ }
+ return valid;
+ }
+
+ if (p_target.kind == GDScriptParser::DataType::ENUM) {
+ if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
+ return true;
+ }
+ if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // From here on the target type is an object, so we have to test polymorphism.
+
+ if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::NIL) {
+ // null is acceptable in object.
+ return true;
+ }
+
+ StringName src_native;
+ Ref<Script> src_script;
+ const GDScriptParser::ClassNode *src_class = nullptr;
+
+ switch (p_source.kind) {
+ case GDScriptParser::DataType::NATIVE:
+ if (p_target.kind != GDScriptParser::DataType::NATIVE) {
+ // Non-native class cannot be supertype of native.
+ return false;
+ }
+ if (p_source.is_meta_type) {
+ src_native = GDScriptNativeClass::get_class_static();
+ } else {
+ src_native = p_source.native_type;
+ }
+ break;
+ case GDScriptParser::DataType::SCRIPT:
+ if (p_target.kind == GDScriptParser::DataType::CLASS) {
+ // A script type cannot be a subtype of a GDScript class.
+ return false;
+ }
+ if (p_source.is_meta_type) {
+ src_native = p_source.script_type->get_class_name();
+ } else {
+ src_script = p_source.script_type;
+ src_native = src_script->get_instance_base_type();
+ }
+ break;
+ case GDScriptParser::DataType::CLASS:
+ if (p_source.is_meta_type) {
+ src_native = GDScript::get_class_static();
+ } else {
+ src_class = p_source.class_type;
+ const GDScriptParser::ClassNode *base = src_class;
+ while (base->base_type.kind == GDScriptParser::DataType::CLASS) {
+ base = base->base_type.class_type;
+ }
+ src_native = base->base_type.native_type;
+ src_script = base->base_type.script_type;
+ }
+ break;
+ case GDScriptParser::DataType::VARIANT:
+ case GDScriptParser::DataType::BUILTIN:
+ case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::ENUM_VALUE:
+ case GDScriptParser::DataType::UNRESOLVED:
+ break; // Already solved before.
+ }
+
+ // Get underscore-prefixed version for some classes.
+ src_native = get_real_class_name(src_native);
+
+ switch (p_target.kind) {
+ case GDScriptParser::DataType::NATIVE: {
+ if (p_target.is_meta_type) {
+ return ClassDB::is_parent_class(src_native, GDScriptNativeClass::get_class_static());
+ }
+ StringName tgt_native = get_real_class_name(p_target.native_type);
+ return ClassDB::is_parent_class(src_native, tgt_native);
+ }
+ case GDScriptParser::DataType::SCRIPT:
+ if (p_target.is_meta_type) {
+ return ClassDB::is_parent_class(src_native, p_target.script_type->get_class_name());
+ }
+ while (src_script.is_valid()) {
+ if (src_script == p_target.script_type) {
+ return true;
+ }
+ src_script = src_script->get_base_script();
+ }
+ return false;
+ case GDScriptParser::DataType::CLASS:
+ if (p_target.is_meta_type) {
+ return ClassDB::is_parent_class(src_native, GDScript::get_class_static());
+ }
+ while (src_class != nullptr) {
+ if (src_class->fqcn == p_target.class_type->fqcn) {
+ return true;
+ }
+ src_class = src_class->base_type.class_type;
+ }
+ return false;
+ case GDScriptParser::DataType::VARIANT:
+ case GDScriptParser::DataType::BUILTIN:
+ case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::ENUM_VALUE:
+ case GDScriptParser::DataType::UNRESOLVED:
+ break; // Already solved before.
+ }
+
+ return false;
+}
+
+void GDScriptAnalyzer::push_error(const String &p_message, const GDScriptParser::Node *p_origin) {
+ mark_node_unsafe(p_origin);
+ parser->push_error(p_message, p_origin);
+}
+
+void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
+#ifdef DEBUG_ENABLED
+ for (int i = p_node->start_line; i <= p_node->end_line; i++) {
+ parser->unsafe_lines.insert(i);
+ }
+#endif
+}
+
+bool GDScriptAnalyzer::class_exists(const StringName &p_class) {
+ StringName real_name = get_real_class_name(p_class);
+ return ClassDB::class_exists(real_name) && ClassDB::is_class_exposed(real_name);
+}
+
+Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
+ Ref<GDScriptParserRef> ref;
+ if (depended_parsers.has(p_path)) {
+ ref = depended_parsers[p_path];
+ } else {
+ Error err = OK;
+ ref = GDScriptCache::get_parser(p_path, GDScriptParserRef::EMPTY, err, parser->script_path);
+ depended_parsers[p_path] = ref;
+ }
+
+ return ref;
+}
+
+Error GDScriptAnalyzer::resolve_inheritance() {
+ return resolve_inheritance(parser->head);
+}
+
+Error GDScriptAnalyzer::resolve_interface() {
+ resolve_class_interface(parser->head);
+ return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+}
+
+Error GDScriptAnalyzer::resolve_body() {
+ resolve_class_body(parser->head);
+ return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+}
+
+Error GDScriptAnalyzer::resolve_program() {
+ resolve_class_interface(parser->head);
+ resolve_class_body(parser->head);
+
+ List<String> parser_keys;
+ depended_parsers.get_key_list(&parser_keys);
+ for (const List<String>::Element *E = parser_keys.front(); E != nullptr; E = E->next()) {
+ if (depended_parsers[E->get()].is_null()) {
+ return ERR_PARSE_ERROR;
+ }
+ depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ }
+ depended_parsers.clear();
+ return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+}
+
+Error GDScriptAnalyzer::analyze() {
+ parser->errors.clear();
+ Error err = resolve_inheritance(parser->head);
+ if (err) {
+ return err;
+ }
+ return resolve_program();
+}
+
+GDScriptAnalyzer::GDScriptAnalyzer(GDScriptParser *p_parser) {
+ parser = p_parser;
+}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
new file mode 100644
index 0000000000..c3911cce76
--- /dev/null
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* gdscript_analyzer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 GDSCRIPT_ANALYZER_H
+#define GDSCRIPT_ANALYZER_H
+
+#include "core/object.h"
+#include "core/reference.h"
+#include "core/set.h"
+#include "gdscript_cache.h"
+#include "gdscript_parser.h"
+
+class GDScriptAnalyzer {
+ GDScriptParser *parser = nullptr;
+ HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
+
+ const GDScriptParser::EnumNode *current_enum = nullptr;
+
+ Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
+ GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
+
+ void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement);
+
+ // This traverses the tree to resolve all TypeNodes.
+ Error resolve_program();
+
+ void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation);
+ void resolve_class_interface(GDScriptParser::ClassNode *p_class);
+ void resolve_class_body(GDScriptParser::ClassNode *p_class);
+ void resolve_function_signature(GDScriptParser::FunctionNode *p_function);
+ void resolve_function_body(GDScriptParser::FunctionNode *p_function);
+ void resolve_node(GDScriptParser::Node *p_node);
+ void resolve_suite(GDScriptParser::SuiteNode *p_suite);
+ void resolve_if(GDScriptParser::IfNode *p_if);
+ void resolve_for(GDScriptParser::ForNode *p_for);
+ void resolve_while(GDScriptParser::WhileNode *p_while);
+ void resolve_variable(GDScriptParser::VariableNode *p_variable);
+ void resolve_constant(GDScriptParser::ConstantNode *p_constant);
+ void resolve_assert(GDScriptParser::AssertNode *p_assert);
+ void resolve_match(GDScriptParser::MatchNode *p_match);
+ void resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test);
+ void resolve_match_pattern(GDScriptParser::PatternNode *p_match_pattern, GDScriptParser::ExpressionNode *p_match_test);
+ void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
+ void resolve_return(GDScriptParser::ReturnNode *p_return);
+
+ // Reduction functions.
+ void reduce_expression(GDScriptParser::ExpressionNode *p_expression);
+ void reduce_array(GDScriptParser::ArrayNode *p_array);
+ void reduce_assignment(GDScriptParser::AssignmentNode *p_assignment);
+ void reduce_await(GDScriptParser::AwaitNode *p_await);
+ void reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_op);
+ void reduce_call(GDScriptParser::CallNode *p_call, bool is_await = false);
+ void reduce_cast(GDScriptParser::CastNode *p_cast);
+ void reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary);
+ void reduce_get_node(GDScriptParser::GetNodeNode *p_get_node);
+ void reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin = false);
+ void reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base = nullptr);
+ void reduce_literal(GDScriptParser::LiteralNode *p_literal);
+ void reduce_preload(GDScriptParser::PreloadNode *p_preload);
+ void reduce_self(GDScriptParser::SelfNode *p_self);
+ void reduce_subscript(GDScriptParser::SubscriptNode *p_subscript);
+ void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op);
+ void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
+
+ // Helpers.
+ GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
+ GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
+ GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const;
+ GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name);
+ bool get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
+ bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
+ bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
+ bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
+ GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
+ bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
+ void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
+ void mark_node_unsafe(const GDScriptParser::Node *p_node);
+ bool class_exists(const StringName &p_class);
+ Ref<GDScriptParserRef> get_parser_for(const String &p_path);
+#ifdef DEBUG_ENABLED
+ bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
+#endif
+
+public:
+ Error resolve_inheritance();
+ Error resolve_interface();
+ Error resolve_body();
+ Error analyze();
+
+ GDScriptAnalyzer(GDScriptParser *p_parser);
+
+ static void cleanup();
+};
+
+#endif // GDSCRIPT_ANALYZER_H
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
new file mode 100644
index 0000000000..8f0ce99de6
--- /dev/null
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -0,0 +1,736 @@
+/*************************************************************************/
+/* gdscript_byte_codegen.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "gdscript_byte_codegen.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "gdscript.h"
+
+uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) {
+#ifdef TOOLS_ENABLED
+ function->arg_names.push_back(p_name);
+#endif
+ function->_argument_count++;
+ function->argument_types.push_back(p_type);
+ if (p_is_optional) {
+ if (function->_default_arg_count == 0) {
+ append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
+ }
+ function->default_arguments.push_back(opcodes.size());
+ function->_default_arg_count++;
+ }
+
+ return add_local(p_name, p_type);
+}
+
+uint32_t GDScriptByteCodeGenerator::add_local(const StringName &p_name, const GDScriptDataType &p_type) {
+ int stack_pos = increase_stack();
+ add_stack_identifier(p_name, stack_pos);
+ return stack_pos;
+}
+
+uint32_t GDScriptByteCodeGenerator::add_local_constant(const StringName &p_name, const Variant &p_constant) {
+ int index = add_or_get_constant(p_constant);
+ local_constants[p_name] = index;
+ return index;
+}
+
+uint32_t GDScriptByteCodeGenerator::add_or_get_constant(const Variant &p_constant) {
+ if (constant_map.has(p_constant)) {
+ return constant_map[p_constant];
+ }
+ int index = constant_map.size();
+ constant_map[p_constant] = index;
+ return index;
+}
+
+uint32_t GDScriptByteCodeGenerator::add_or_get_name(const StringName &p_name) {
+ return get_name_map_pos(p_name);
+}
+
+uint32_t GDScriptByteCodeGenerator::add_temporary() {
+ current_temporaries++;
+ return increase_stack();
+}
+
+void GDScriptByteCodeGenerator::pop_temporary() {
+ current_stack_size--;
+ current_temporaries--;
+}
+
+void GDScriptByteCodeGenerator::start_parameters() {}
+
+void GDScriptByteCodeGenerator::end_parameters() {
+ function->default_arguments.invert();
+}
+
+void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) {
+ function = memnew(GDScriptFunction);
+ debug_stack = EngineDebugger::is_active();
+
+ function->name = p_function_name;
+ function->_script = p_script;
+ function->source = p_script->get_path();
+
+#ifdef DEBUG_ENABLED
+ function->func_cname = (String(function->source) + " - " + String(p_function_name)).utf8();
+ function->_func_cname = function->func_cname.get_data();
+#endif
+
+ function->_static = p_static;
+ function->return_type = p_return_type;
+ function->rpc_mode = p_rpc_mode;
+ function->_argument_count = 0;
+}
+
+GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
+ append(GDScriptFunction::OPCODE_END);
+
+ if (constant_map.size()) {
+ function->_constant_count = constant_map.size();
+ function->constants.resize(constant_map.size());
+ function->_constants_ptr = function->constants.ptrw();
+ const Variant *K = nullptr;
+ while ((K = constant_map.next(K))) {
+ int idx = constant_map[*K];
+ function->constants.write[idx] = *K;
+ }
+ } else {
+ function->_constants_ptr = nullptr;
+ function->_constant_count = 0;
+ }
+
+ if (name_map.size()) {
+ function->global_names.resize(name_map.size());
+ function->_global_names_ptr = &function->global_names[0];
+ for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) {
+ function->global_names.write[E->get()] = E->key();
+ }
+ function->_global_names_count = function->global_names.size();
+
+ } else {
+ function->_global_names_ptr = nullptr;
+ function->_global_names_count = 0;
+ }
+
+ if (opcodes.size()) {
+ function->code = opcodes;
+ function->_code_ptr = &function->code[0];
+ function->_code_size = opcodes.size();
+
+ } else {
+ function->_code_ptr = nullptr;
+ function->_code_size = 0;
+ }
+
+ if (function->default_arguments.size()) {
+ function->_default_arg_count = function->default_arguments.size();
+ function->_default_arg_ptr = &function->default_arguments[0];
+ } else {
+ function->_default_arg_count = 0;
+ function->_default_arg_ptr = nullptr;
+ }
+
+ if (debug_stack) {
+ function->stack_debug = stack_debug;
+ }
+ function->_stack_size = stack_max;
+ function->_call_size = call_max;
+
+ ended = true;
+ return function;
+}
+
+#ifdef DEBUG_ENABLED
+void GDScriptByteCodeGenerator::set_signature(const String &p_signature) {
+ function->profile.signature = p_signature;
+}
+#endif
+
+void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
+ function->_initial_line = p_line;
+}
+
+void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
+ append(GDScriptFunction::OPCODE_OPERATOR);
+ append(p_operator);
+ append(p_left_operand);
+ append(p_right_operand);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) {
+ append(GDScriptFunction::OPCODE_EXTENDS_TEST);
+ append(p_source);
+ append(p_type);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
+ append(GDScriptFunction::OPCODE_IS_BUILTIN);
+ append(p_source);
+ append(p_type);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_left_operand);
+ logic_op_jump_pos1.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_right_operand);
+ logic_op_jump_pos2.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
+ // If here means both operands are true.
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(p_target);
+ // Jump away from the fail condition.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(opcodes.size() + 3);
+ // Here it means one of operands is false.
+ patch_jump(logic_op_jump_pos1.back()->get());
+ patch_jump(logic_op_jump_pos2.back()->get());
+ logic_op_jump_pos1.pop_back();
+ logic_op_jump_pos2.pop_back();
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_or_left_operand(const Address &p_left_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(p_left_operand);
+ logic_op_jump_pos1.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(p_right_operand);
+ logic_op_jump_pos2.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
+ // If here means both operands are false.
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(p_target);
+ // Jump away from the success condition.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(opcodes.size() + 3);
+ // Here it means one of operands is false.
+ patch_jump(logic_op_jump_pos1.back()->get());
+ patch_jump(logic_op_jump_pos2.back()->get());
+ logic_op_jump_pos1.pop_back();
+ logic_op_jump_pos2.pop_back();
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_start_ternary(const Address &p_target) {
+ ternary_result.push_back(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_ternary_condition(const Address &p_condition) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_condition);
+ ternary_jump_fail_pos.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(ternary_result.back()->get());
+ append(p_expr);
+ // Jump away from the false path.
+ append(GDScriptFunction::OPCODE_JUMP);
+ ternary_jump_skip_pos.push_back(opcodes.size());
+ append(0);
+ // Fail must jump here.
+ patch_jump(ternary_jump_fail_pos.back()->get());
+ ternary_jump_fail_pos.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr) {
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(ternary_result.back()->get());
+ append(p_expr);
+}
+
+void GDScriptByteCodeGenerator::write_end_ternary() {
+ patch_jump(ternary_jump_skip_pos.back()->get());
+ ternary_jump_skip_pos.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_SET);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+}
+
+void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address &p_index, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_GET);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_SET_NAMED);
+ append(p_target);
+ append(p_name);
+ append(p_source);
+}
+
+void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_GET_NAMED);
+ append(p_source);
+ append(p_name);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_set_member(const Address &p_value, const StringName &p_name) {
+ append(GDScriptFunction::OPCODE_SET_MEMBER);
+ append(p_name);
+ append(p_value);
+}
+
+void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const StringName &p_name) {
+ append(GDScriptFunction::OPCODE_GET_MEMBER);
+ append(p_name);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
+ if (p_target.type.has_type && !p_source.type.has_type) {
+ // Typed assignment.
+ switch (p_target.type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
+ append(p_target.type.builtin_type);
+ append(p_target);
+ append(p_source);
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE);
+ append(class_idx);
+ append(p_target);
+ append(p_source);
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = p_target.type.script_type;
+ int idx = get_constant_pos(script);
+
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT);
+ append(idx);
+ append(p_target);
+ append(p_source);
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(p_target);
+ append(p_source);
+ }
+ }
+ } else {
+ if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
+ // Need conversion..
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
+ append(p_target.type.builtin_type);
+ append(p_target);
+ append(p_source);
+ } else {
+ // Either untyped assignment or already type-checked by the parser
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(p_target);
+ append(p_source);
+ }
+ }
+}
+
+void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
+ switch (p_type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
+ append(p_type.builtin_type);
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_CAST_TO_NATIVE);
+ append(class_idx);
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = p_type.script_type;
+ int idx = get_constant_pos(script);
+
+ append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT);
+ append(idx);
+ } break;
+ default: {
+ return;
+ }
+ }
+
+ append(p_source);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_SELF_BASE);
+ append(p_function_name);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_ASYNC);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ append(p_function);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_method->get_name());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_method->get_name());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT);
+ append(p_type);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
+ append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
+ append(GDScriptFunction::OPCODE_AWAIT);
+ append(p_operand);
+ append(GDScriptFunction::OPCODE_AWAIT_RESUME);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_if(const Address &p_condition) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_condition);
+ if_jmp_addrs.push_back(opcodes.size());
+ append(0); // Jump destination, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_else() {
+ append(GDScriptFunction::OPCODE_JUMP); // Jump from true if block;
+ int else_jmp_addr = opcodes.size();
+ append(0); // Jump destination, will be patched.
+
+ patch_jump(if_jmp_addrs.back()->get());
+ if_jmp_addrs.pop_back();
+ if_jmp_addrs.push_back(else_jmp_addr);
+}
+
+void GDScriptByteCodeGenerator::write_endif() {
+ patch_jump(if_jmp_addrs.back()->get());
+ if_jmp_addrs.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) {
+ int counter_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ int container_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+
+ current_breaks_to_patch.push_back(List<int>());
+
+ // Assign container.
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(container_pos);
+ append(p_list);
+
+ // Begin loop.
+ append(GDScriptFunction::OPCODE_ITERATE_BEGIN);
+ append(counter_pos);
+ append(container_pos);
+ for_jmp_addrs.push_back(opcodes.size());
+ append(0); // End of loop address, will be patched.
+ append(p_variable);
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(opcodes.size() + 6); // Skip over 'continue' code.
+
+ // Next iteration.
+ int continue_addr = opcodes.size();
+ continue_addrs.push_back(continue_addr);
+ append(GDScriptFunction::OPCODE_ITERATE);
+ append(counter_pos);
+ append(container_pos);
+ for_jmp_addrs.push_back(opcodes.size());
+ append(0); // Jump destination, will be patched.
+ append(p_variable);
+}
+
+void GDScriptByteCodeGenerator::write_endfor() {
+ // Jump back to loop check.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(continue_addrs.back()->get());
+ continue_addrs.pop_back();
+
+ // Patch end jumps (two of them).
+ for (int i = 0; i < 2; i++) {
+ patch_jump(for_jmp_addrs.back()->get());
+ for_jmp_addrs.pop_back();
+ }
+
+ // Patch break statements.
+ for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ current_breaks_to_patch.pop_back();
+
+ current_stack_size -= 2; // Remove loop temporaries.
+}
+
+void GDScriptByteCodeGenerator::start_while_condition() {
+ current_breaks_to_patch.push_back(List<int>());
+ continue_addrs.push_back(opcodes.size());
+}
+
+void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
+ // Condition check.
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_condition);
+ while_jmp_addrs.push_back(opcodes.size());
+ append(0); // End of loop address, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_endwhile() {
+ // Jump back to loop check.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(continue_addrs.back()->get());
+ continue_addrs.pop_back();
+
+ // Patch end jump.
+ patch_jump(while_jmp_addrs.back()->get());
+ while_jmp_addrs.pop_back();
+
+ // Patch break statements.
+ for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ current_breaks_to_patch.pop_back();
+}
+
+void GDScriptByteCodeGenerator::start_match() {
+ match_continues_to_patch.push_back(List<int>());
+}
+
+void GDScriptByteCodeGenerator::start_match_branch() {
+ // Patch continue statements.
+ for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ match_continues_to_patch.pop_back();
+ // Start a new list for next branch.
+ match_continues_to_patch.push_back(List<int>());
+}
+
+void GDScriptByteCodeGenerator::end_match() {
+ // Patch continue statements.
+ for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ match_continues_to_patch.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_break() {
+ append(GDScriptFunction::OPCODE_JUMP);
+ current_breaks_to_patch.back()->get().push_back(opcodes.size());
+ append(0);
+}
+
+void GDScriptByteCodeGenerator::write_continue() {
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(continue_addrs.back()->get());
+}
+
+void GDScriptByteCodeGenerator::write_continue_match() {
+ append(GDScriptFunction::OPCODE_JUMP);
+ match_continues_to_patch.back()->get().push_back(opcodes.size());
+ append(0);
+}
+
+void GDScriptByteCodeGenerator::write_breakpoint() {
+ append(GDScriptFunction::OPCODE_BREAKPOINT);
+}
+
+void GDScriptByteCodeGenerator::write_newline(int p_line) {
+ append(GDScriptFunction::OPCODE_LINE);
+ append(p_line);
+ current_line = p_line;
+}
+
+void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
+ append(GDScriptFunction::OPCODE_RETURN);
+ append(p_return_value);
+}
+
+void GDScriptByteCodeGenerator::write_assert(const Address &p_test, const Address &p_message) {
+ append(GDScriptFunction::OPCODE_ASSERT);
+ append(p_test);
+ append(p_message);
+}
+
+void GDScriptByteCodeGenerator::start_block() {
+ push_stack_identifiers();
+}
+
+void GDScriptByteCodeGenerator::end_block() {
+ pop_stack_identifiers();
+}
+
+GDScriptByteCodeGenerator::~GDScriptByteCodeGenerator() {
+ if (!ended && function != nullptr) {
+ memdelete(function);
+ }
+}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
new file mode 100644
index 0000000000..62438b6dd2
--- /dev/null
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -0,0 +1,277 @@
+/*************************************************************************/
+/* gdscript_byte_codegen.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 GDSCRIPT_BYTE_CODEGEN
+#define GDSCRIPT_BYTE_CODEGEN
+
+#include "gdscript_codegen.h"
+
+class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
+ bool ended = false;
+ GDScriptFunction *function = nullptr;
+ bool debug_stack = false;
+
+ Vector<int> opcodes;
+ List<Map<StringName, int>> stack_id_stack;
+ Map<StringName, int> stack_identifiers;
+ Map<StringName, int> local_constants;
+
+ List<GDScriptFunction::StackDebug> stack_debug;
+ List<Map<StringName, int>> block_identifier_stack;
+ Map<StringName, int> block_identifiers;
+
+ int current_stack_size = 0;
+ int current_temporaries = 0;
+
+ HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
+ Map<StringName, int> name_map;
+#ifdef TOOLS_ENABLED
+ Vector<StringName> named_globals;
+#endif
+ int current_line = 0;
+ int stack_max = 0;
+ int call_max = 0;
+
+ List<int> if_jmp_addrs; // List since this can be nested.
+ List<int> for_jmp_addrs;
+ List<int> while_jmp_addrs;
+ List<int> continue_addrs;
+
+ // Used to patch jumps with `and` and `or` operators with short-circuit.
+ List<int> logic_op_jump_pos1;
+ List<int> logic_op_jump_pos2;
+
+ List<Address> ternary_result;
+ List<int> ternary_jump_fail_pos;
+ List<int> ternary_jump_skip_pos;
+
+ List<List<int>> current_breaks_to_patch;
+ List<List<int>> match_continues_to_patch;
+
+ void add_stack_identifier(const StringName &p_id, int p_stackpos) {
+ stack_identifiers[p_id] = p_stackpos;
+ if (debug_stack) {
+ block_identifiers[p_id] = p_stackpos;
+ GDScriptFunction::StackDebug sd;
+ sd.added = true;
+ sd.line = current_line;
+ sd.identifier = p_id;
+ sd.pos = p_stackpos;
+ stack_debug.push_back(sd);
+ }
+ }
+
+ void push_stack_identifiers() {
+ stack_id_stack.push_back(stack_identifiers);
+ if (debug_stack) {
+ block_identifier_stack.push_back(block_identifiers);
+ block_identifiers.clear();
+ }
+ }
+
+ void pop_stack_identifiers() {
+ stack_identifiers = stack_id_stack.back()->get();
+ current_stack_size = stack_identifiers.size() + current_temporaries;
+ stack_id_stack.pop_back();
+
+ if (debug_stack) {
+ for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) {
+ GDScriptFunction::StackDebug sd;
+ sd.added = false;
+ sd.identifier = E->key();
+ sd.line = current_line;
+ sd.pos = E->get();
+ stack_debug.push_back(sd);
+ }
+ block_identifiers = block_identifier_stack.back()->get();
+ block_identifier_stack.pop_back();
+ }
+ }
+
+ int get_name_map_pos(const StringName &p_identifier) {
+ int ret;
+ if (!name_map.has(p_identifier)) {
+ ret = name_map.size();
+ name_map[p_identifier] = ret;
+ } else {
+ ret = name_map[p_identifier];
+ }
+ return ret;
+ }
+
+ int get_constant_pos(const Variant &p_constant) {
+ if (constant_map.has(p_constant))
+ return constant_map[p_constant];
+ int pos = constant_map.size();
+ constant_map[p_constant] = pos;
+ return pos;
+ }
+
+ void alloc_stack(int p_level) {
+ if (p_level >= stack_max)
+ stack_max = p_level + 1;
+ }
+
+ void alloc_call(int p_params) {
+ if (p_params >= call_max)
+ call_max = p_params;
+ }
+
+ int increase_stack() {
+ int top = current_stack_size++;
+ alloc_stack(current_stack_size);
+ return top;
+ }
+
+ int address_of(const Address &p_address) {
+ switch (p_address.mode) {
+ case Address::SELF:
+ return GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS;
+ case Address::CLASS:
+ return GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS;
+ case Address::MEMBER:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
+ case Address::CLASS_CONSTANT:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS);
+ case Address::LOCAL_CONSTANT:
+ case Address::CONSTANT:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
+ case Address::LOCAL_VARIABLE:
+ case Address::TEMPORARY:
+ case Address::FUNCTION_PARAMETER:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ case Address::GLOBAL:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
+ case Address::NAMED_GLOBAL:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL << GDScriptFunction::ADDR_BITS);
+ case Address::NIL:
+ return GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
+ }
+ return -1; // Unreachable.
+ }
+
+ void append(int code) {
+ opcodes.push_back(code);
+ }
+
+ void append(const Address &p_address) {
+ opcodes.push_back(address_of(p_address));
+ }
+
+ void append(const StringName &p_name) {
+ opcodes.push_back(get_name_map_pos(p_name));
+ }
+
+ void patch_jump(int p_address) {
+ opcodes.write[p_address] = opcodes.size();
+ }
+
+public:
+ virtual uint32_t add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) override;
+ virtual uint32_t add_local(const StringName &p_name, const GDScriptDataType &p_type) override;
+ virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) override;
+ virtual uint32_t add_or_get_constant(const Variant &p_constant) override;
+ virtual uint32_t add_or_get_name(const StringName &p_name) override;
+ virtual uint32_t add_temporary() override;
+ virtual void pop_temporary() override;
+
+ virtual void start_parameters() override;
+ virtual void end_parameters() override;
+
+ virtual void start_block() override;
+ virtual void end_block() override;
+
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) override;
+ virtual GDScriptFunction *write_end() override;
+
+#ifdef DEBUG_ENABLED
+ virtual void set_signature(const String &p_signature) override;
+#endif
+ virtual void set_initial_line(int p_line) override;
+
+ virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
+ virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) override;
+ virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) override;
+ virtual void write_and_left_operand(const Address &p_left_operand) override;
+ virtual void write_and_right_operand(const Address &p_right_operand) override;
+ virtual void write_end_and(const Address &p_target) override;
+ virtual void write_or_left_operand(const Address &p_left_operand) override;
+ virtual void write_or_right_operand(const Address &p_right_operand) override;
+ virtual void write_end_or(const Address &p_target) override;
+ virtual void write_start_ternary(const Address &p_target) override;
+ virtual void write_ternary_condition(const Address &p_condition) override;
+ virtual void write_ternary_true_expr(const Address &p_expr) override;
+ virtual void write_ternary_false_expr(const Address &p_expr) override;
+ virtual void write_end_ternary() override;
+ virtual void write_set(const Address &p_target, const Address &p_index, const Address &p_source) override;
+ virtual void write_get(const Address &p_target, const Address &p_index, const Address &p_source) override;
+ virtual void write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) override;
+ virtual void write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) override;
+ virtual void write_set_member(const Address &p_value, const StringName &p_name) override;
+ virtual void write_get_member(const Address &p_target, const StringName &p_name) override;
+ virtual void write_assign(const Address &p_target, const Address &p_source) override;
+ virtual void write_assign_true(const Address &p_target) override;
+ virtual void write_assign_false(const Address &p_target) override;
+ virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
+ virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
+ virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
+ virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) override;
+ virtual void write_await(const Address &p_target, const Address &p_operand) override;
+ virtual void write_if(const Address &p_condition) override;
+ virtual void write_else() override;
+ virtual void write_endif() override;
+ virtual void write_for(const Address &p_variable, const Address &p_list) override;
+ virtual void write_endfor() override;
+ virtual void start_while_condition() override;
+ virtual void write_while(const Address &p_condition) override;
+ virtual void write_endwhile() override;
+ virtual void start_match() override;
+ virtual void start_match_branch() override;
+ virtual void end_match() override;
+ virtual void write_break() override;
+ virtual void write_continue() override;
+ virtual void write_continue_match() override;
+ virtual void write_breakpoint() override;
+ virtual void write_newline(int p_line) override;
+ virtual void write_return(const Address &p_return_value) override;
+ virtual void write_assert(const Address &p_test, const Address &p_message) override;
+
+ virtual ~GDScriptByteCodeGenerator();
+};
+
+#endif // GDSCRIPT_BYTE_CODEGEN
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
new file mode 100644
index 0000000000..57b95f5b21
--- /dev/null
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -0,0 +1,255 @@
+/*************************************************************************/
+/* gdscript_cache.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "gdscript_cache.h"
+
+#include "core/os/file_access.h"
+#include "core/vector.h"
+#include "gdscript.h"
+#include "gdscript_analyzer.h"
+#include "gdscript_parser.h"
+
+bool GDScriptParserRef::is_valid() const {
+ return parser != nullptr;
+}
+
+GDScriptParserRef::Status GDScriptParserRef::get_status() const {
+ return status;
+}
+
+GDScriptParser *GDScriptParserRef::get_parser() const {
+ return parser;
+}
+
+Error GDScriptParserRef::raise_status(Status p_new_status) {
+ ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA);
+
+ Error result = OK;
+
+ while (p_new_status > status) {
+ switch (status) {
+ case EMPTY:
+ result = parser->parse(GDScriptCache::get_source_code(path), path, false);
+ status = PARSED;
+ break;
+ case PARSED: {
+ analyzer = memnew(GDScriptAnalyzer(parser));
+ Error inheritance_result = analyzer->resolve_inheritance();
+ status = INHERITANCE_SOLVED;
+ if (result == OK) {
+ result = inheritance_result;
+ }
+ } break;
+ case INHERITANCE_SOLVED: {
+ Error interface_result = analyzer->resolve_interface();
+ status = INTERFACE_SOLVED;
+ if (result == OK) {
+ result = interface_result;
+ }
+ } break;
+ case INTERFACE_SOLVED: {
+ Error body_result = analyzer->resolve_body();
+ status = FULLY_SOLVED;
+ if (result == OK) {
+ result = body_result;
+ }
+ } break;
+ case FULLY_SOLVED: {
+ return result;
+ }
+ }
+ if (result != OK) {
+ if (parser != nullptr) {
+ memdelete(parser);
+ parser = nullptr;
+ }
+ if (analyzer != nullptr) {
+ memdelete(analyzer);
+ analyzer = nullptr;
+ }
+ return result;
+ }
+ }
+
+ return result;
+}
+
+GDScriptParserRef::~GDScriptParserRef() {
+ if (parser != nullptr) {
+ memdelete(parser);
+ }
+ if (analyzer != nullptr) {
+ memdelete(analyzer);
+ }
+ MutexLock lock(GDScriptCache::singleton->lock);
+ GDScriptCache::singleton->parser_map.erase(path);
+}
+
+GDScriptCache *GDScriptCache::singleton = nullptr;
+
+void GDScriptCache::remove_script(const String &p_path) {
+ MutexLock lock(singleton->lock);
+ singleton->shallow_gdscript_cache.erase(p_path);
+ singleton->full_gdscript_cache.erase(p_path);
+}
+
+Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptParserRef::Status p_status, Error &r_error, const String &p_owner) {
+ MutexLock lock(singleton->lock);
+ Ref<GDScriptParserRef> ref;
+ if (p_owner != String()) {
+ singleton->dependencies[p_owner].insert(p_path);
+ }
+ if (singleton->parser_map.has(p_path)) {
+ ref = Ref<GDScriptParserRef>(singleton->parser_map[p_path]);
+ } else {
+ if (!FileAccess::exists(p_path)) {
+ r_error = ERR_FILE_NOT_FOUND;
+ return ref;
+ }
+ GDScriptParser *parser = memnew(GDScriptParser);
+ ref.instance();
+ ref->parser = parser;
+ ref->path = p_path;
+ singleton->parser_map[p_path] = ref.ptr();
+ }
+
+ r_error = ref->raise_status(p_status);
+
+ return ref;
+}
+
+String GDScriptCache::get_source_code(const String &p_path) {
+ Vector<uint8_t> source_file;
+ Error err;
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (err) {
+ ERR_FAIL_COND_V(err, "");
+ }
+
+ int len = f->get_len();
+ source_file.resize(len + 1);
+ int r = f->get_buffer(source_file.ptrw(), len);
+ f->close();
+ ERR_FAIL_COND_V(r != len, "");
+ source_file.write[len] = 0;
+
+ String source;
+ if (source.parse_utf8((const char *)source_file.ptr())) {
+ ERR_FAIL_V_MSG("", "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
+ }
+ return source;
+}
+
+Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const String &p_owner) {
+ MutexLock lock(singleton->lock);
+ if (p_owner != String()) {
+ singleton->dependencies[p_owner].insert(p_path);
+ }
+ if (singleton->full_gdscript_cache.has(p_path)) {
+ return singleton->full_gdscript_cache[p_path];
+ }
+ if (singleton->shallow_gdscript_cache.has(p_path)) {
+ return singleton->shallow_gdscript_cache[p_path];
+ }
+
+ Ref<GDScript> script;
+ script.instance();
+ script->set_path(p_path, true);
+ script->set_script_path(p_path);
+ script->load_source_code(p_path);
+
+ singleton->shallow_gdscript_cache[p_path] = script.ptr();
+ return script;
+}
+
+Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner) {
+ MutexLock lock(singleton->lock);
+
+ if (p_owner != String()) {
+ singleton->dependencies[p_owner].insert(p_path);
+ }
+
+ r_error = OK;
+ if (singleton->full_gdscript_cache.has(p_path)) {
+ return singleton->full_gdscript_cache[p_path];
+ }
+ Ref<GDScript> script = get_shallow_script(p_path);
+
+ r_error = script->load_source_code(p_path);
+
+ if (r_error) {
+ return script;
+ }
+
+ r_error = script->reload();
+ if (r_error) {
+ return script;
+ }
+
+ singleton->full_gdscript_cache[p_path] = script.ptr();
+ singleton->shallow_gdscript_cache.erase(p_path);
+
+ return script;
+}
+
+Error GDScriptCache::finish_compiling(const String &p_owner) {
+ // Mark this as compiled.
+ Ref<GDScript> script = get_shallow_script(p_owner);
+ singleton->full_gdscript_cache[p_owner] = script.ptr();
+ singleton->shallow_gdscript_cache.erase(p_owner);
+
+ Set<String> depends = singleton->dependencies[p_owner];
+
+ Error err = OK;
+ for (const Set<String>::Element *E = depends.front(); E != nullptr; E = E->next()) {
+ Error this_err = OK;
+ // No need to save the script. We assume it's already referenced in the owner.
+ get_full_script(E->get(), this_err);
+
+ if (this_err != OK) {
+ err = this_err;
+ }
+ }
+
+ singleton->dependencies.erase(p_owner);
+
+ return err;
+}
+
+GDScriptCache::GDScriptCache() {
+ singleton = this;
+}
+
+GDScriptCache::~GDScriptCache() {
+ parser_map.clear();
+ shallow_gdscript_cache.clear();
+ full_gdscript_cache.clear();
+ singleton = nullptr;
+}
diff --git a/modules/mono/glue/gd_glue.h b/modules/gdscript/gdscript_cache.h
index 3ad6058205..865df34051 100644
--- a/modules/mono/glue/gd_glue.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gd_glue.h */
+/* gdscript_cache.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,59 +28,70 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GD_GLUE_H
-#define GD_GLUE_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects);
-
-MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type);
-
-int godot_icall_GD_hash(MonoObject *p_var);
-
-MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id);
-
-void godot_icall_GD_print(MonoArray *p_what);
-
-void godot_icall_GD_printerr(MonoArray *p_what);
-
-void godot_icall_GD_printraw(MonoArray *p_what);
-
-void godot_icall_GD_prints(MonoArray *p_what);
-
-void godot_icall_GD_printt(MonoArray *p_what);
-
-float godot_icall_GD_randf();
-
-uint32_t godot_icall_GD_randi();
-
-void godot_icall_GD_randomize();
-
-double godot_icall_GD_rand_range(double from, double to);
-
-uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed);
-
-void godot_icall_GD_seed(uint64_t p_seed);
-
-MonoString *godot_icall_GD_str(MonoArray *p_what);
-
-MonoObject *godot_icall_GD_str2var(MonoString *p_str);
-
-MonoBoolean godot_icall_GD_type_exists(StringName *p_type);
-
-MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects);
-
-MonoString *godot_icall_GD_var2str(MonoObject *p_var);
-
-MonoObject *godot_icall_DefaultGodotTaskScheduler();
-
-// Register internal calls
-
-void godot_register_gd_icalls();
-
-#endif // MONO_GLUE_ENABLED
-
-#endif // GD_GLUE_H
+#ifndef GDSCRIPT_CACHE_H
+#define GDSCRIPT_CACHE_H
+
+#include "core/hash_map.h"
+#include "core/os/mutex.h"
+#include "core/reference.h"
+#include "core/set.h"
+#include "gdscript.h"
+
+class GDScriptAnalyzer;
+class GDScriptParser;
+
+class GDScriptParserRef : public Reference {
+public:
+ enum Status {
+ EMPTY,
+ PARSED,
+ INHERITANCE_SOLVED,
+ INTERFACE_SOLVED,
+ FULLY_SOLVED,
+ };
+
+private:
+ GDScriptParser *parser = nullptr;
+ GDScriptAnalyzer *analyzer = nullptr;
+ Status status = EMPTY;
+ String path;
+
+ friend class GDScriptCache;
+
+public:
+ bool is_valid() const;
+ Status get_status() const;
+ GDScriptParser *get_parser() const;
+ Error raise_status(Status p_new_status);
+
+ GDScriptParserRef() {}
+ ~GDScriptParserRef();
+};
+
+class GDScriptCache {
+ // String key is full path.
+ HashMap<String, GDScriptParserRef *> parser_map;
+ HashMap<String, GDScript *> shallow_gdscript_cache;
+ HashMap<String, GDScript *> full_gdscript_cache;
+ HashMap<String, Set<String>> dependencies;
+
+ friend class GDScript;
+ friend class GDScriptParserRef;
+
+ static GDScriptCache *singleton;
+
+ Mutex lock;
+ static void remove_script(const String &p_path);
+
+public:
+ static Ref<GDScriptParserRef> get_parser(const String &p_path, GDScriptParserRef::Status status, Error &r_error, const String &p_owner = String());
+ static String get_source_code(const String &p_path);
+ static Ref<GDScript> get_shallow_script(const String &p_path, const String &p_owner = String());
+ static Ref<GDScript> get_full_script(const String &p_path, Error &r_error, const String &p_owner = String());
+ static Error finish_compiling(const String &p_owner);
+
+ GDScriptCache();
+ ~GDScriptCache();
+};
+
+#endif // GDSCRIPT_CACHE_H
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
new file mode 100644
index 0000000000..31e1e6ba23
--- /dev/null
+++ b/modules/gdscript/gdscript_codegen.h
@@ -0,0 +1,160 @@
+/*************************************************************************/
+/* gdscript_codegen.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 GDSCRIPT_CODEGEN
+#define GDSCRIPT_CODEGEN
+
+#include "core/io/multiplayer_api.h"
+#include "core/string_name.h"
+#include "core/variant.h"
+#include "gdscript_function.h"
+#include "gdscript_functions.h"
+
+class GDScriptCodeGenerator {
+public:
+ struct Address {
+ enum AddressMode {
+ SELF,
+ CLASS,
+ MEMBER,
+ CONSTANT,
+ CLASS_CONSTANT,
+ LOCAL_CONSTANT,
+ LOCAL_VARIABLE,
+ FUNCTION_PARAMETER,
+ TEMPORARY,
+ GLOBAL,
+ NAMED_GLOBAL,
+ NIL,
+ };
+ AddressMode mode = NIL;
+ uint32_t address = 0;
+ GDScriptDataType type;
+
+ Address() {}
+ Address(AddressMode p_mode, const GDScriptDataType &p_type = GDScriptDataType()) {
+ mode = p_mode;
+ type = p_type;
+ }
+ Address(AddressMode p_mode, uint32_t p_address, const GDScriptDataType &p_type = GDScriptDataType()) {
+ mode = p_mode,
+ address = p_address;
+ type = p_type;
+ }
+ };
+
+ virtual uint32_t add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) = 0;
+ virtual uint32_t add_local(const StringName &p_name, const GDScriptDataType &p_type) = 0;
+ virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) = 0;
+ virtual uint32_t add_or_get_constant(const Variant &p_constant) = 0;
+ virtual uint32_t add_or_get_name(const StringName &p_name) = 0;
+ virtual uint32_t add_temporary() = 0;
+ virtual void pop_temporary() = 0;
+
+ virtual void start_parameters() = 0;
+ virtual void end_parameters() = 0;
+
+ virtual void start_block() = 0;
+ virtual void end_block() = 0;
+
+ // virtual int get_max_stack_level() = 0;
+ // virtual int get_max_function_arguments() = 0;
+
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) = 0;
+ virtual GDScriptFunction *write_end() = 0;
+
+#ifdef DEBUG_ENABLED
+ virtual void set_signature(const String &p_signature) = 0;
+#endif
+ virtual void set_initial_line(int p_line) = 0;
+
+ // virtual void alloc_stack(int p_level) = 0; // Is this needed?
+ // virtual void alloc_call(int p_arg_count) = 0; // This might be automatic from other functions.
+
+ virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
+ virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) = 0;
+ virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) = 0;
+ virtual void write_and_left_operand(const Address &p_left_operand) = 0;
+ virtual void write_and_right_operand(const Address &p_right_operand) = 0;
+ virtual void write_end_and(const Address &p_target) = 0;
+ virtual void write_or_left_operand(const Address &p_left_operand) = 0;
+ virtual void write_or_right_operand(const Address &p_right_operand) = 0;
+ virtual void write_end_or(const Address &p_target) = 0;
+ virtual void write_start_ternary(const Address &p_target) = 0;
+ virtual void write_ternary_condition(const Address &p_condition) = 0;
+ virtual void write_ternary_true_expr(const Address &p_expr) = 0;
+ virtual void write_ternary_false_expr(const Address &p_expr) = 0;
+ virtual void write_end_ternary() = 0;
+ virtual void write_set(const Address &p_target, const Address &p_index, const Address &p_source) = 0;
+ virtual void write_get(const Address &p_target, const Address &p_index, const Address &p_source) = 0;
+ virtual void write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) = 0;
+ virtual void write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) = 0;
+ virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0;
+ virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0;
+ virtual void write_assign(const Address &p_target, const Address &p_source) = 0;
+ virtual void write_assign_true(const Address &p_target) = 0;
+ virtual void write_assign_false(const Address &p_target) = 0;
+ virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
+ virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
+ virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
+ virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) = 0;
+ virtual void write_await(const Address &p_target, const Address &p_operand) = 0;
+ virtual void write_if(const Address &p_condition) = 0;
+ // virtual void write_elseif(const Address &p_condition) = 0; This kind of makes things more difficult for no real benefit.
+ virtual void write_else() = 0;
+ virtual void write_endif() = 0;
+ virtual void write_for(const Address &p_variable, const Address &p_list) = 0;
+ virtual void write_endfor() = 0;
+ virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation.
+ virtual void write_while(const Address &p_condition) = 0;
+ virtual void write_endwhile() = 0;
+ virtual void start_match() = 0;
+ virtual void start_match_branch() = 0;
+ virtual void end_match() = 0;
+ virtual void write_break() = 0;
+ virtual void write_continue() = 0;
+ virtual void write_continue_match() = 0;
+ virtual void write_breakpoint() = 0;
+ virtual void write_newline(int p_line) = 0;
+ virtual void write_return(const Address &p_return_value) = 0;
+ virtual void write_assert(const Address &p_test, const Address &p_message) = 0;
+
+ virtual ~GDScriptCodeGenerator() {}
+};
+
+#endif // GDSCRIPT_CODEGEN
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index bc095ae1f9..bad450c9f9 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -31,13 +31,15 @@
#include "gdscript_compiler.h"
#include "gdscript.h"
+#include "gdscript_byte_codegen.h"
+#include "gdscript_cache.h"
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
- if (codegen.function_node && codegen.function_node->_static) {
+ if (codegen.function_node && codegen.function_node->is_static) {
return false;
}
- if (codegen.stack_identifiers.has(p_name)) {
+ if (codegen.locals.has(p_name)) {
return false; //shadowed
}
@@ -66,55 +68,16 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
error = p_error;
if (p_node) {
- err_line = p_node->line;
- err_column = p_node->column;
+ err_line = p_node->start_line;
+ err_column = p_node->leftmost_column;
} else {
err_line = 0;
err_column = 0;
}
}
-bool GDScriptCompiler::_create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level) {
- ERR_FAIL_COND_V(on->arguments.size() != 1, false);
-
- int src_address_a = _parse_expression(codegen, on->arguments[0], p_stack_level);
- if (src_address_a < 0) {
- return false;
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR); // perform operator
- codegen.opcodes.push_back(op); //which operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(src_address_a); // argument 2 (repeated)
- //codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter)
- return true;
-}
-
-bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer, int p_index_addr) {
- ERR_FAIL_COND_V(on->arguments.size() != 2, false);
-
- int src_address_a = _parse_expression(codegen, on->arguments[0], p_stack_level, false, p_initializer, p_index_addr);
- if (src_address_a < 0) {
- return false;
- }
- if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- p_stack_level++; //uses stack for return, increase stack
- }
-
- int src_address_b = _parse_expression(codegen, on->arguments[1], p_stack_level, false, p_initializer);
- if (src_address_b < 0) {
- return false;
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR); // perform operator
- codegen.opcodes.push_back(op); //which operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
- return true;
-}
-
-GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
- if (!p_datatype.has_type) {
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) const {
+ if (!p_datatype.is_set() || !p_datatype.is_hard_type()) {
return GDScriptDataType();
}
@@ -122,6 +85,9 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.has_type = true;
switch (p_datatype.kind) {
+ case GDScriptParser::DataType::VARIANT: {
+ result.has_type = false;
+ } break;
case GDScriptParser::DataType::BUILTIN: {
result.kind = GDScriptDataType::BUILTIN;
result.builtin_type = p_datatype.builtin_type;
@@ -132,158 +98,124 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
- result.script_type = p_datatype.script_type;
- result.native_type = result.script_type->get_instance_base_type();
- } break;
- case GDScriptParser::DataType::GDSCRIPT: {
- result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = p_datatype.script_type;
+ result.script_type = Ref<Script>(p_datatype.script_type).ptr();
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
// Locate class by constructing the path to it and following that path
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
- List<StringName> names;
- while (class_type->owner) {
- names.push_back(class_type->name);
- class_type = class_type->owner;
- }
+ if (class_type) {
+ if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.empty() && class_type->fqcn.begins_with(main_script->name))) {
+ // Local class.
+ List<StringName> names;
+ while (class_type->outer) {
+ names.push_back(class_type->identifier->name);
+ class_type = class_type->outer;
+ }
- Ref<GDScript> script = Ref<GDScript>(main_script);
- while (names.back()) {
- if (!script->subclasses.has(names.back()->get())) {
- ERR_PRINT("Parser bug: Cannot locate datatype class.");
- result.has_type = false;
- return GDScriptDataType();
+ Ref<GDScript> script = Ref<GDScript>(main_script);
+ while (names.back()) {
+ if (!script->subclasses.has(names.back()->get())) {
+ ERR_PRINT("Parser bug: Cannot locate datatype class.");
+ result.has_type = false;
+ return GDScriptDataType();
+ }
+ script = script->subclasses[names.back()->get()];
+ names.pop_back();
+ }
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.script_type = script.ptr();
+ result.native_type = script->get_instance_base_type();
+ } else {
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path).ptr();
+ result.native_type = p_datatype.native_type;
}
- script = script->subclasses[names.back()->get()];
- names.pop_back();
}
-
- result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = script;
- result.native_type = script->get_instance_base_type();
} break;
- default: {
+ case GDScriptParser::DataType::ENUM_VALUE:
+ result.has_type = true;
+ result.kind = GDScriptDataType::BUILTIN;
+ result.builtin_type = Variant::INT;
+ break;
+ case GDScriptParser::DataType::ENUM:
+ result.has_type = true;
+ result.kind = GDScriptDataType::BUILTIN;
+ result.builtin_type = Variant::DICTIONARY;
+ break;
+ case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
return GDScriptDataType();
}
}
- return result;
-}
-
-int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level, int p_index_addr) {
- Variant::Operator var_op = Variant::OP_MAX;
-
- switch (p_expression->op) {
- case GDScriptParser::OperatorNode::OP_ASSIGN_ADD:
- var_op = Variant::OP_ADD;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_SUB:
- var_op = Variant::OP_SUBTRACT;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_MUL:
- var_op = Variant::OP_MULTIPLY;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_DIV:
- var_op = Variant::OP_DIVIDE;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_MOD:
- var_op = Variant::OP_MODULE;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- var_op = Variant::OP_SHIFT_LEFT;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- var_op = Variant::OP_SHIFT_RIGHT;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_AND:
- var_op = Variant::OP_BIT_AND;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_OR:
- var_op = Variant::OP_BIT_OR;
- break;
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_XOR:
- var_op = Variant::OP_BIT_XOR;
- break;
- case GDScriptParser::OperatorNode::OP_INIT_ASSIGN:
- case GDScriptParser::OperatorNode::OP_ASSIGN: {
- //none
- } break;
- default: {
- ERR_FAIL_V(-1);
- }
+ // Only hold strong reference to the script if it's not the owner of the
+ // element qualified with this type, to avoid cyclic references (leaks).
+ if (result.script_type && result.script_type != p_owner) {
+ result.script_type_ref = Ref<Script>(result.script_type);
}
- bool initializer = p_expression->op == GDScriptParser::OperatorNode::OP_INIT_ASSIGN;
-
- if (var_op == Variant::OP_MAX) {
- return _parse_expression(codegen, p_expression->arguments[1], p_stack_level, false, initializer);
- }
+ return result;
+}
- if (!_create_binary_operator(codegen, p_expression, var_op, p_stack_level, initializer, p_index_addr)) {
- return -1;
+GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
+ if (p_expression->is_constant) {
+ return codegen.add_constant(p_expression->reduced_value);
}
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
-}
+ GDScriptCodeGenerator *gen = codegen.generator;
-int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root, bool p_initializer, int p_index_addr) {
switch (p_expression->type) {
- //should parse variable declaration and adjust stack accordingly...
- case GDScriptParser::Node::TYPE_IDENTIFIER: {
- //return identifier
- //wait, identifier could be a local variable or something else... careful here, must reference properly
- //as stack may be more interesting to work with
-
- //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases performance a lot.
-
+ case GDScriptParser::Node::IDENTIFIER: {
+ // Look for identifiers in current scope.
const GDScriptParser::IdentifierNode *in = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
StringName identifier = in->name;
- // TRY STACK!
- if (!p_initializer && codegen.stack_identifiers.has(identifier)) {
- int pos = codegen.stack_identifiers[identifier];
- return pos | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS);
+ // Try function parameters.
+ if (codegen.parameters.has(identifier)) {
+ return codegen.parameters[identifier];
+ }
+
+ // Try local variables and constants.
+ if (!p_initializer && codegen.locals.has(identifier)) {
+ return codegen.locals[identifier];
}
- // TRY CLASS MEMBER
+ // Try class members.
if (_is_class_member_property(codegen, identifier)) {
- //get property
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET_MEMBER); // perform operator
- codegen.opcodes.push_back(codegen.get_name_map_pos(identifier)); // argument 2 (unary only takes one parameter)
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
- }
-
- //TRY MEMBERS!
- if (!codegen.function_node || !codegen.function_node->_static) {
- // TRY MEMBER VARIABLES!
- //static function
+ // Get property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Could get the type of the class member here.
+ gen->write_get_member(temp, identifier);
+ return temp;
+ }
+
+ // Try members.
+ if (!codegen.function_node || !codegen.function_node->is_static) {
+ // Try member variables.
if (codegen.script->member_indices.has(identifier)) {
- int idx = codegen.script->member_indices[identifier].index;
- return idx | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
+ // Perform getter.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary();
+ Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
+ gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
+ return temp;
+ } else {
+ // No getter or inside getter: direct member access.,
+ int idx = codegen.script->member_indices[identifier].index;
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
+ }
}
}
- //TRY CLASS CONSTANTS
-
+ // Try class constants.
GDScript *owner = codegen.script;
while (owner) {
GDScript *scr = owner;
GDScriptNativeClass *nc = nullptr;
while (scr) {
if (scr->constants.has(identifier)) {
- //int idx=scr->constants[identifier];
- int idx = codegen.get_name_map_pos(identifier);
- return idx | (GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here.
}
if (scr->native.is_valid()) {
nc = scr->native.ptr();
@@ -291,1512 +223,1639 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
scr = scr->_base;
}
- // CLASS C++ Integer Constant
-
+ // Class C++ integer constant.
if (nc) {
bool success = false;
int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
if (success) {
- Variant key = constant;
- int idx;
-
- if (!codegen.constant_map.has(key)) {
- idx = codegen.constant_map.size();
- codegen.constant_map[key] = idx;
-
- } else {
- idx = codegen.constant_map[key];
- }
-
- return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
+ return codegen.add_constant(constant);
}
}
owner = owner->_owner;
}
+ // Try signals and methods (can be made callables);
+ if (codegen.class_node->members_indices.has(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+
+ gen->write_get_named(temp, identifier, self);
+ return temp;
+ }
+ }
+
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
- return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::GLOBAL, idx); // TODO: Get type.
}
- /* TRY GLOBAL CLASSES */
-
+ // Try global classes.
if (ScriptServer::is_global_class(identifier)) {
const GDScriptParser::ClassNode *class_node = codegen.class_node;
- while (class_node->owner) {
- class_node = class_node->owner;
- }
-
- if (class_node->name == identifier) {
- _set_error("Using own name in class file is not allowed (creates a cyclic reference)", p_expression);
- return -1;
- }
-
- RES res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
- if (res.is_null()) {
- _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
- return -1;
+ while (class_node->outer) {
+ class_node = class_node->outer;
}
- Variant key = res;
- int idx;
-
- if (!codegen.constant_map.has(key)) {
- idx = codegen.constant_map.size();
- codegen.constant_map[key] = idx;
+ RES res;
+ if (class_node->identifier && class_node->identifier->name == identifier) {
+ res = Ref<GDScript>(main_script);
} else {
- idx = codegen.constant_map[key];
+ res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
+ if (res.is_null()) {
+ _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
}
- return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
+ return codegen.add_constant(res);
}
#ifdef TOOLS_ENABLED
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
- int idx = codegen.named_globals.find(identifier);
- if (idx == -1) {
- idx = codegen.named_globals.size();
- codegen.named_globals.push_back(identifier);
- }
- return idx | (GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL << GDScriptFunction::ADDR_BITS);
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NAMED_GLOBAL, gen->add_or_get_name(identifier)); // TODO: Get type.
}
#endif
- //not found, error
-
+ // Not found, error.
_set_error("Identifier not found: " + String(identifier), p_expression);
-
- return -1;
-
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
} break;
- case GDScriptParser::Node::TYPE_CONSTANT: {
- //return constant
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_expression);
-
- int idx;
-
- if (!codegen.constant_map.has(cn->value)) {
- idx = codegen.constant_map.size();
- codegen.constant_map[cn->value] = idx;
-
- } else {
- idx = codegen.constant_map[cn->value];
- }
-
- return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ case GDScriptParser::Node::LITERAL: {
+ // Return constant.
+ const GDScriptParser::LiteralNode *cn = static_cast<const GDScriptParser::LiteralNode *>(p_expression);
+ return codegen.add_constant(cn->value);
} break;
- case GDScriptParser::Node::TYPE_SELF: {
+ case GDScriptParser::Node::SELF: {
//return constant
- if (codegen.function_node && codegen.function_node->_static) {
+ if (codegen.function_node && codegen.function_node->is_static) {
_set_error("'self' not present in static function!", p_expression);
- return -1;
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
- return (GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF);
} break;
- case GDScriptParser::Node::TYPE_ARRAY: {
+ case GDScriptParser::Node::ARRAY: {
const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
- Vector<int> values;
+ Vector<GDScriptCodeGenerator::Address> values;
- int slevel = p_stack_level;
+ // Create the result temporary first since it's the last to be killed.
+ GDScriptDataType array_type;
+ array_type.has_type = true;
+ array_type.kind = GDScriptDataType::BUILTIN;
+ array_type.builtin_type = Variant::ARRAY;
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);
for (int i = 0; i < an->elements.size(); i++) {
- int ret = _parse_expression(codegen, an->elements[i], slevel);
- if (ret < 0) {
- return ret;
+ GDScriptCodeGenerator::Address val = _parse_expression(codegen, r_error, an->elements[i]);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
-
- values.push_back(ret);
+ values.push_back(val);
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
- codegen.opcodes.push_back(values.size());
+ gen->write_construct_array(result, values);
+
for (int i = 0; i < values.size(); i++) {
- codegen.opcodes.push_back(values[i]);
+ if (values[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
}
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
-
+ return result;
} break;
- case GDScriptParser::Node::TYPE_DICTIONARY: {
+ case GDScriptParser::Node::DICTIONARY: {
const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
- Vector<int> values;
+ Vector<GDScriptCodeGenerator::Address> elements;
- int slevel = p_stack_level;
+ // Create the result temporary first since it's the last to be killed.
+ GDScriptDataType dict_type;
+ dict_type.has_type = true;
+ dict_type.kind = GDScriptDataType::BUILTIN;
+ dict_type.builtin_type = Variant::DICTIONARY;
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(dict_type);
for (int i = 0; i < dn->elements.size(); i++) {
- int ret = _parse_expression(codegen, dn->elements[i].key, slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ // Key.
+ GDScriptCodeGenerator::Address element;
+ switch (dn->style) {
+ case GDScriptParser::DictionaryNode::PYTHON_DICT:
+ // Python-style: key is any expression.
+ element = _parse_expression(codegen, r_error, dn->elements[i].key);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ break;
+ case GDScriptParser::DictionaryNode::LUA_TABLE:
+ // Lua-style: key is an identifier interpreted as string.
+ String key = static_cast<const GDScriptParser::IdentifierNode *>(dn->elements[i].key)->name;
+ element = codegen.add_constant(key);
+ break;
}
- values.push_back(ret);
+ elements.push_back(element);
- ret = _parse_expression(codegen, dn->elements[i].value, slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ element = _parse_expression(codegen, r_error, dn->elements[i].value);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- values.push_back(ret);
+ elements.push_back(element);
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
- codegen.opcodes.push_back(dn->elements.size());
- for (int i = 0; i < values.size(); i++) {
- codegen.opcodes.push_back(values[i]);
- }
+ gen->write_construct_dictionary(result, elements);
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
+ for (int i = 0; i < elements.size(); i++) {
+ if (elements[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ }
+ return result;
} break;
- case GDScriptParser::Node::TYPE_CAST: {
+ case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
+ GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type->get_datatype());
- int slevel = p_stack_level;
- int src_addr = _parse_expression(codegen, cn->source_node, slevel);
- if (src_addr < 0) {
- return src_addr;
- }
- if (src_addr & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ // Create temporary for result first since it will be deleted last.
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
- GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type);
+ GDScriptCodeGenerator::Address source = _parse_expression(codegen, r_error, cn->operand);
- switch (cast_type.kind) {
- case GDScriptDataType::BUILTIN: {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
- codegen.opcodes.push_back(cast_type.builtin_type);
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx;
- if (GDScriptLanguage::get_singleton()->get_global_map().has(cast_type.native_type)) {
- class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cast_type.native_type];
- class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
- } else {
- _set_error("Invalid native class type '" + String(cast_type.native_type) + "'.", cn);
- return -1;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
- codegen.opcodes.push_back(class_idx); // variable type
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = cast_type.script_type;
- int idx = codegen.get_constant_pos(script);
- idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
- codegen.opcodes.push_back(idx); // variable type
- } break;
- default: {
- _set_error("Parser bug: unresolved data type.", cn);
- return -1;
- }
- }
+ gen->write_cast(result, source, cast_type);
- codegen.opcodes.push_back(src_addr); // source address
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
+ if (source.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ return source;
} break;
- case GDScriptParser::Node::TYPE_OPERATOR: {
- //hell breaks loose
-
- const GDScriptParser::OperatorNode *on = static_cast<const GDScriptParser::OperatorNode *>(p_expression);
- switch (on->op) {
- //call/constructor operator
- case GDScriptParser::OperatorNode::OP_PARENT_CALL: {
- ERR_FAIL_COND_V(on->arguments.size() < 1, -1);
-
- const GDScriptParser::IdentifierNode *in = (const GDScriptParser::IdentifierNode *)on->arguments[0];
-
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 1; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
- }
+ case GDScriptParser::Node::CALL: {
+ const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
+ GDScriptDataType type = _gdtype_from_datatype(call->get_datatype());
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(type);
+
+ Vector<GDScriptCodeGenerator::Address> arguments;
+ for (int i = 0; i < call->arguments.size(); i++) {
+ GDScriptCodeGenerator::Address arg = _parse_expression(codegen, r_error, call->arguments[i]);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ arguments.push_back(arg);
+ }
- //push call bytecode
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_SELF_BASE); // basic type constructor
+ if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
+ // Construct a built-in type.
+ Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- codegen.opcodes.push_back(codegen.get_name_map_pos(in->name)); //instance
- codegen.opcodes.push_back(arguments.size()); //argument count
- codegen.alloc_call(arguments.size());
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]); //arguments
- }
+ gen->write_construct(result, vtype, arguments);
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
+ // Built-in function.
+ GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
+ gen->write_call_builtin(result, func, arguments);
+ } else {
+ // Regular function.
+ const GDScriptParser::ExpressionNode *callee = call->callee;
- } break;
- case GDScriptParser::OperatorNode::OP_CALL: {
- if (on->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
- //construct a basic type
- ERR_FAIL_COND_V(on->arguments.size() < 1, -1);
-
- const GDScriptParser::TypeNode *tn = (const GDScriptParser::TypeNode *)on->arguments[0];
- int vtype = tn->vtype;
-
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 1; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
+ if (call->is_super) {
+ // Super call.
+ gen->write_super_call(result, call->function_name, arguments);
+ } else {
+ if (callee->type == GDScriptParser::Node::IDENTIFIER) {
+ // Self function call.
+ if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
+ GDScriptCodeGenerator::Address self;
+ self.mode = GDScriptCodeGenerator::Address::CLASS;
+ gen->write_call(result, self, call->function_name, arguments);
+ } else {
+ gen->write_call_self(result, call->function_name, arguments);
}
+ } else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
- //push call bytecode
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT); // basic type constructor
- codegen.opcodes.push_back(vtype); //instance
- codegen.opcodes.push_back(arguments.size()); //argument count
- codegen.alloc_call(arguments.size());
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]); //arguments
+ if (subscript->is_attribute) {
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ if (within_await) {
+ gen->write_call_async(result, base, call->function_name, arguments);
+ } else {
+ gen->write_call(result, base, call->function_name, arguments);
+ }
+ if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ } else {
+ _set_error("Cannot call something that isn't a function.", call->callee);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
+ } else {
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ }
+ }
- } else if (on->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
- //built in function
-
- ERR_FAIL_COND_V(on->arguments.size() < 1, -1);
+ for (int i = 0; i < arguments.size(); i++) {
+ if (arguments[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ }
+ return result;
+ } break;
+ case GDScriptParser::Node::GET_NODE: {
+ const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression);
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 1; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
+ String node_name;
+ if (get_node->string != nullptr) {
+ node_name += String(get_node->string->value);
+ } else {
+ for (int i = 0; i < get_node->chain.size(); i++) {
+ if (i > 0) {
+ node_name += "/";
+ }
+ node_name += get_node->chain[i]->name;
+ }
+ }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ Vector<GDScriptCodeGenerator::Address> args;
+ args.push_back(codegen.add_constant(NodePath(node_name)));
- arguments.push_back(ret);
- }
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(static_cast<const GDScriptParser::BuiltInFunctionNode *>(on->arguments[0])->function);
- codegen.opcodes.push_back(on->arguments.size() - 1);
- codegen.alloc_call(on->arguments.size() - 1);
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]);
- }
+ MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
+ gen->write_call_method_bind(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
- } else {
- //regular function
- ERR_FAIL_COND_V(on->arguments.size() < 2, -1);
-
- const GDScriptParser::Node *instance = on->arguments[0];
+ return result;
+ } break;
+ case GDScriptParser::Node::PRELOAD: {
+ const GDScriptParser::PreloadNode *preload = static_cast<const GDScriptParser::PreloadNode *>(p_expression);
- if (instance->type == GDScriptParser::Node::TYPE_SELF) {
- //room for optimization
- }
+ // Add resource as constant.
+ return codegen.add_constant(preload->resource);
+ } break;
+ case GDScriptParser::Node::AWAIT: {
+ const GDScriptParser::AwaitNode *await = static_cast<const GDScriptParser::AwaitNode *>(p_expression);
+
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype()));
+ within_await = true;
+ GDScriptCodeGenerator::Address argument = _parse_expression(codegen, r_error, await->to_await);
+ within_await = false;
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- Vector<int> arguments;
- int slevel = p_stack_level;
+ gen->write_await(result, argument);
- for (int i = 0; i < on->arguments.size(); i++) {
- int ret;
+ if (argument.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- if (i == 0 && on->arguments[i]->type == GDScriptParser::Node::TYPE_SELF && codegen.function_node && codegen.function_node->_static) {
- //static call to self
- ret = (GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS);
- } else if (i == 1) {
- if (on->arguments[i]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
- _set_error("Attempt to call a non-identifier.", on);
- return -1;
- }
- GDScriptParser::IdentifierNode *id = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[i]);
- ret = codegen.get_name_map_pos(id->name);
+ return result;
+ } break;
+ // Indexing operator.
+ case GDScriptParser::Node::SUBSCRIPT: {
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype()));
+
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- } else {
- ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- }
- arguments.push_back(ret);
- }
+ bool named = subscript->is_attribute;
+ StringName name;
+ GDScriptCodeGenerator::Address index;
+ if (p_index_addr.mode != GDScriptCodeGenerator::Address::NIL) {
+ index = p_index_addr;
+ } else if (subscript->is_attribute) {
+ if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
+ GDScriptParser::IdentifierNode *identifier = subscript->attribute;
+ const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
- codegen.opcodes.push_back(p_root ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN); // perform operator
- codegen.opcodes.push_back(on->arguments.size() - 2);
- codegen.alloc_call(on->arguments.size() - 2);
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]);
- }
- }
- } break;
- case GDScriptParser::OperatorNode::OP_YIELD: {
- ERR_FAIL_COND_V(on->arguments.size() && on->arguments.size() != 2, -1);
-
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 0; i < on->arguments.size(); i++) {
- int ret = _parse_expression(codegen, on->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
+#ifdef DEBUG_ENABLED
+ if (MI && MI->get().getter == codegen.function_name) {
+ String n = identifier->name;
+ _set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", identifier);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
+#endif
- //push call bytecode
- codegen.opcodes.push_back(arguments.size() == 0 ? GDScriptFunction::OPCODE_YIELD : GDScriptFunction::OPCODE_YIELD_SIGNAL); // basic type constructor
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]); //arguments
+ if (MI && MI->get().getter == "") {
+ // Remove result temp as we don't need it.
+ gen->pop_temporary();
+ // Faster than indexing self (as if no self. had been used).
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->get().index, _gdtype_from_datatype(subscript->get_datatype()));
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_YIELD_RESUME);
- //next will be where to place the result :)
+ }
- } break;
+ name = subscript->attribute->name;
+ named = true;
+ } else {
+ if (subscript->index->type == GDScriptParser::Node::LITERAL && static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value.get_type() == Variant::STRING) {
+ // Also, somehow, named (speed up anyway).
+ name = static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value;
+ named = true;
+ } else {
+ // Regular indexing.
+ index = _parse_expression(codegen, r_error, subscript->index);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ }
+ }
- //indexing operator
- case GDScriptParser::OperatorNode::OP_INDEX:
- case GDScriptParser::OperatorNode::OP_INDEX_NAMED: {
- ERR_FAIL_COND_V(on->arguments.size() != 2, -1);
+ if (named) {
+ gen->write_get_named(result, name, base);
+ } else {
+ gen->write_get(result, index, base);
+ }
- int slevel = p_stack_level;
- bool named = (on->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED);
+ if (index.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- int from = _parse_expression(codegen, on->arguments[0], slevel);
- if (from < 0) {
- return from;
- }
+ return result;
+ } break;
+ case GDScriptParser::Node::UNARY_OPERATOR: {
+ const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression);
- int index;
- if (p_index_addr != 0) {
- index = p_index_addr;
- } else if (named) {
- if (on->arguments[0]->type == GDScriptParser::Node::TYPE_SELF && codegen.script && codegen.function_node && !codegen.function_node->_static) {
- GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[1]);
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary();
-#ifdef DEBUG_ENABLED
- if (MI && MI->get().getter == codegen.function_node->name) {
- String n = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[1])->name;
- _set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", on);
- return -1;
- }
-#endif
+ GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, unary->operand);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- if (MI && MI->get().getter == "") {
- // Faster than indexing self (as if no self. had been used)
- return (MI->get().index) | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
- }
- }
+ gen->write_operator(result, unary->variant_op, operand, GDScriptCodeGenerator::Address());
- index = codegen.get_name_map_pos(static_cast<GDScriptParser::IdentifierNode *>(on->arguments[1])->name);
+ if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- } else {
- if (on->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT && static_cast<const GDScriptParser::ConstantNode *>(on->arguments[1])->value.get_type() == Variant::STRING) {
- //also, somehow, named (speed up anyway)
- StringName name = static_cast<const GDScriptParser::ConstantNode *>(on->arguments[1])->value;
- index = codegen.get_name_map_pos(name);
- named = true;
+ return result;
+ }
+ case GDScriptParser::Node::BINARY_OPERATOR: {
+ const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
- } else {
- //regular indexing
- if (from & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ GDScriptCodeGenerator::Address result = codegen.add_temporary();
- index = _parse_expression(codegen, on->arguments[1], slevel);
- if (index < 0) {
- return index;
- }
- }
- }
+ switch (binary->operation) {
+ case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: {
+ // AND operator with early out on failure.
+ GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
+ gen->write_and_left_operand(left_operand);
+ GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
+ gen->write_and_right_operand(right_operand);
- codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET); // perform operator
- codegen.opcodes.push_back(from); // argument 1
- codegen.opcodes.push_back(index); // argument 2 (unary only takes one parameter)
+ gen->write_end_and(result);
+ if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
} break;
- case GDScriptParser::OperatorNode::OP_AND: {
- // AND operator with early out on failure
+ case GDScriptParser::BinaryOpNode::OP_LOGIC_OR: {
+ // OR operator with early out on success.
+ GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
+ gen->write_or_left_operand(left_operand);
+ GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
+ gen->write_or_right_operand(right_operand);
+
+ gen->write_end_or(result);
- int res = _parse_expression(codegen, on->arguments[0], p_stack_level);
- if (res < 0) {
- return res;
+ if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(res);
- int jump_fail_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- res = _parse_expression(codegen, on->arguments[1], p_stack_level);
- if (res < 0) {
- return res;
+ if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
+ } break;
+ case GDScriptParser::BinaryOpNode::OP_TYPE_TEST: {
+ GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, binary->left_operand);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(res);
- int jump_fail_pos2 = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- codegen.alloc_stack(p_stack_level); //it will be used..
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TRUE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
- codegen.opcodes.write[jump_fail_pos2] = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_FALSE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
-
+ if (binary->right_operand->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name) != Variant::VARIANT_MAX) {
+ // `is` with builtin type)
+ Variant::Type type = GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name);
+ gen->write_type_test_builtin(result, operand, type);
+ } else {
+ GDScriptCodeGenerator::Address type = _parse_expression(codegen, r_error, binary->right_operand);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_type_test(result, operand, type);
+ if (type.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ }
} break;
- case GDScriptParser::OperatorNode::OP_OR: {
- // OR operator with early out on success
+ default: {
+ GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
+ GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
+
+ gen->write_operator(result, binary->variant_op, left_operand, right_operand);
- int res = _parse_expression(codegen, on->arguments[0], p_stack_level);
- if (res < 0) {
- return res;
+ if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF);
- codegen.opcodes.push_back(res);
- int jump_success_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- res = _parse_expression(codegen, on->arguments[1], p_stack_level);
- if (res < 0) {
- return res;
+ if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
+ }
+ }
+ return result;
+ } break;
+ case GDScriptParser::Node::TERNARY_OPERATOR: {
+ // x IF a ELSE y operator with early out on failure.
+ const GDScriptParser::TernaryOpNode *ternary = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(ternary->get_datatype()));
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF);
- codegen.opcodes.push_back(res);
- int jump_success_pos2 = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- codegen.alloc_stack(p_stack_level); //it will be used..
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_FALSE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- codegen.opcodes.write[jump_success_pos] = codegen.opcodes.size();
- codegen.opcodes.write[jump_success_pos2] = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TRUE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ gen->write_start_ternary(result);
- } break;
- // ternary operators
- case GDScriptParser::OperatorNode::OP_TERNARY_IF: {
- // x IF a ELSE y operator with early out on failure
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, r_error, ternary->condition);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_ternary_condition(condition);
- int res = _parse_expression(codegen, on->arguments[0], p_stack_level);
- if (res < 0) {
- return res;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(res);
- int jump_fail_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- res = _parse_expression(codegen, on->arguments[1], p_stack_level);
- if (res < 0) {
- return res;
- }
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- codegen.alloc_stack(p_stack_level); //it will be used..
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(res);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- int jump_past_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
- res = _parse_expression(codegen, on->arguments[2], p_stack_level);
- if (res < 0) {
- return res;
- }
+ GDScriptCodeGenerator::Address true_expr = _parse_expression(codegen, r_error, ternary->true_expr);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_ternary_true_expr(true_expr);
+ if (true_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(res);
+ GDScriptCodeGenerator::Address false_expr = _parse_expression(codegen, r_error, ternary->false_expr);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_ternary_false_expr(false_expr);
+ if (false_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- codegen.opcodes.write[jump_past_pos] = codegen.opcodes.size();
+ gen->write_end_ternary();
- return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ return result;
+ } break;
+ case GDScriptParser::Node::ASSIGNMENT: {
+ const GDScriptParser::AssignmentNode *assignment = static_cast<const GDScriptParser::AssignmentNode *>(p_expression);
- } break;
- //unary operators
- case GDScriptParser::OperatorNode::OP_NEG: {
- if (!_create_unary_operator(codegen, on, Variant::OP_NEGATE, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_POS: {
- if (!_create_unary_operator(codegen, on, Variant::OP_POSITIVE, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_NOT: {
- if (!_create_unary_operator(codegen, on, Variant::OP_NOT, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
- if (!_create_unary_operator(codegen, on, Variant::OP_BIT_NEGATE, p_stack_level)) {
- return -1;
- }
- } break;
- //binary operators (in precedence order)
- case GDScriptParser::OperatorNode::OP_IN: {
- if (!_create_binary_operator(codegen, on, Variant::OP_IN, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_NOT_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_NOT_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_LESS: {
- if (!_create_binary_operator(codegen, on, Variant::OP_LESS, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_LESS_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_LESS_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_GREATER: {
- if (!_create_binary_operator(codegen, on, Variant::OP_GREATER, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_GREATER_EQUAL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_GREATER_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_ADD: {
- if (!_create_binary_operator(codegen, on, Variant::OP_ADD, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_SUB: {
- if (!_create_binary_operator(codegen, on, Variant::OP_SUBTRACT, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_MUL: {
- if (!_create_binary_operator(codegen, on, Variant::OP_MULTIPLY, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_DIV: {
- if (!_create_binary_operator(codegen, on, Variant::OP_DIVIDE, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_MOD: {
- if (!_create_binary_operator(codegen, on, Variant::OP_MODULE, p_stack_level)) {
- return -1;
- }
- } break;
- //case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break;
- //case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break;
- case GDScriptParser::OperatorNode::OP_BIT_AND: {
- if (!_create_binary_operator(codegen, on, Variant::OP_BIT_AND, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_BIT_OR: {
- if (!_create_binary_operator(codegen, on, Variant::OP_BIT_OR, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_BIT_XOR: {
- if (!_create_binary_operator(codegen, on, Variant::OP_BIT_XOR, p_stack_level)) {
- return -1;
- }
- } break;
- //shift
- case GDScriptParser::OperatorNode::OP_SHIFT_LEFT: {
- if (!_create_binary_operator(codegen, on, Variant::OP_SHIFT_LEFT, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT: {
- if (!_create_binary_operator(codegen, on, Variant::OP_SHIFT_RIGHT, p_stack_level)) {
- return -1;
- }
- } break;
- //assignment operators
- case GDScriptParser::OperatorNode::OP_ASSIGN_ADD:
- case GDScriptParser::OperatorNode::OP_ASSIGN_SUB:
- case GDScriptParser::OperatorNode::OP_ASSIGN_MUL:
- case GDScriptParser::OperatorNode::OP_ASSIGN_DIV:
- case GDScriptParser::OperatorNode::OP_ASSIGN_MOD:
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- case GDScriptParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_AND:
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_OR:
- case GDScriptParser::OperatorNode::OP_ASSIGN_BIT_XOR:
- case GDScriptParser::OperatorNode::OP_INIT_ASSIGN:
- case GDScriptParser::OperatorNode::OP_ASSIGN: {
- ERR_FAIL_COND_V(on->arguments.size() != 2, -1);
-
- if (on->arguments[0]->type == GDScriptParser::Node::TYPE_OPERATOR && (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX || static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED)) {
- // SET (chained) MODE!
+ if (assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) {
+ // SET (chained) MODE!
+ const GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(assignment->assignee);
#ifdef DEBUG_ENABLED
- if (static_cast<GDScriptParser::OperatorNode *>(on->arguments[0])->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- const GDScriptParser::OperatorNode *inon = static_cast<GDScriptParser::OperatorNode *>(on->arguments[0]);
-
- if (inon->arguments[0]->type == GDScriptParser::Node::TYPE_SELF && codegen.script && codegen.function_node && !codegen.function_node->_static) {
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(static_cast<GDScriptParser::IdentifierNode *>(inon->arguments[1])->name);
- if (MI && MI->get().setter == codegen.function_node->name) {
- String n = static_cast<GDScriptParser::IdentifierNode *>(inon->arguments[1])->name;
- _set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", inon);
- return -1;
+ if (subscript->is_attribute && subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
+ const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(subscript->attribute->name);
+ if (MI && MI->get().setter == codegen.function_name) {
+ String n = subscript->attribute->name;
+ _set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", subscript);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ }
+#endif
+ /* Find chain of sets */
+
+ StringName assign_property;
+
+ List<const GDScriptParser::SubscriptNode *> chain;
+
+ {
+ // Create get/set chain.
+ const GDScriptParser::SubscriptNode *n = subscript;
+ while (true) {
+ chain.push_back(n);
+ if (n->base->type != GDScriptParser::Node::SUBSCRIPT) {
+ // Check for a built-in property.
+ if (n->base->type == GDScriptParser::Node::IDENTIFIER) {
+ GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(n->base);
+ if (_is_class_member_property(codegen, identifier->name)) {
+ assign_property = identifier->name;
}
}
+ break;
}
-#endif
+ n = static_cast<const GDScriptParser::SubscriptNode *>(n->base);
+ }
+ }
- int slevel = p_stack_level;
+ /* Chain of gets */
- GDScriptParser::OperatorNode *op = static_cast<GDScriptParser::OperatorNode *>(on->arguments[0]);
+ // Get at (potential) root stack pos, so it can be returned.
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- /* Find chain of sets */
+ GDScriptCodeGenerator::Address prev_base = base;
- StringName assign_property;
+ struct ChainInfo {
+ bool is_named = false;
+ GDScriptCodeGenerator::Address base;
+ GDScriptCodeGenerator::Address key;
+ StringName name;
+ };
- List<GDScriptParser::OperatorNode *> chain;
+ List<ChainInfo> set_chain;
- {
- //create get/set chain
- GDScriptParser::OperatorNode *n = op;
- while (true) {
- chain.push_back(n);
- if (n->arguments[0]->type != GDScriptParser::Node::TYPE_OPERATOR) {
- //check for a built-in property
- if (n->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(n->arguments[0]);
- if (_is_class_member_property(codegen, identifier->name)) {
- assign_property = identifier->name;
- }
- }
- break;
- }
- n = static_cast<GDScriptParser::OperatorNode *>(n->arguments[0]);
- if (n->op != GDScriptParser::OperatorNode::OP_INDEX && n->op != GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- break;
- }
- }
+ for (List<const GDScriptParser::SubscriptNode *>::Element *E = chain.back(); E; E = E->prev()) {
+ if (E == chain.front()) {
+ // Skip the main subscript, since we'll assign to that.
+ break;
+ }
+ const GDScriptParser::SubscriptNode *subscript_elem = E->get();
+ GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript_elem->get_datatype()));
+ GDScriptCodeGenerator::Address key;
+ StringName name;
+
+ if (subscript_elem->is_attribute) {
+ name = subscript_elem->attribute->name;
+ gen->write_get_named(value, name, prev_base);
+ } else {
+ key = _parse_expression(codegen, r_error, subscript_elem->index);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
+ gen->write_get(value, key, prev_base);
+ }
- /* Chain of gets */
+ // Store base and key for setting it back later.
+ set_chain.push_front({ subscript_elem->is_attribute, prev_base, key, name }); // Push to front to invert the list.
+ prev_base = value;
+ }
- //get at (potential) root stack pos, so it can be returned
- int prev_pos = _parse_expression(codegen, chain.back()->get()->arguments[0], slevel);
- if (prev_pos < 0) {
- return prev_pos;
- }
- int retval = prev_pos;
+ // Get value to assign.
+ GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ // Get the key if needed.
+ GDScriptCodeGenerator::Address key;
+ StringName name;
+ if (subscript->is_attribute) {
+ name = subscript->attribute->name;
+ } else {
+ key = _parse_expression(codegen, r_error, subscript->index);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ }
- if (retval & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ // Perform operator if any.
+ if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ GDScriptCodeGenerator::Address value = codegen.add_temporary();
+ if (subscript->is_attribute) {
+ gen->write_get_named(value, name, prev_base);
+ } else {
+ gen->write_get(value, key, prev_base);
+ }
+ gen->write_operator(value, assignment->variant_op, value, assigned);
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ assigned = value;
+ }
+
+ // Perform assignment.
+ if (subscript->is_attribute) {
+ gen->write_set_named(prev_base, name, assigned);
+ } else {
+ gen->write_set(prev_base, key, assigned);
+ }
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- Vector<int> setchain;
+ assigned = prev_base;
- if (assign_property != StringName()) {
- // recover and assign at the end, this allows stuff like
- // position.x+=2.0
- // in Node2D
- setchain.push_back(prev_pos);
- setchain.push_back(codegen.get_name_map_pos(assign_property));
- setchain.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
+ // Set back the values into their bases.
+ for (List<ChainInfo>::Element *E = set_chain.front(); E; E = E->next()) {
+ const ChainInfo &info = E->get();
+ if (!info.is_named) {
+ gen->write_set(info.base, info.key, assigned);
+ if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
+ } else {
+ gen->write_set_named(info.base, info.name, assigned);
+ }
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ assigned = info.base;
+ }
- for (List<GDScriptParser::OperatorNode *>::Element *E = chain.back(); E; E = E->prev()) {
- if (E == chain.front()) { //ignore first
- break;
- }
+ // If this is a local member, also assign to it.
+ // This allow things like: position.x += 2.0
+ if (assign_property != StringName()) {
+ gen->write_set_member(assigned, assign_property);
+ }
- bool named = E->get()->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED;
- int key_idx;
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ } else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) {
+ // Assignment to member property.
+ GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ GDScriptCodeGenerator::Address assign_temp = assigned;
- if (named) {
- key_idx = codegen.get_name_map_pos(static_cast<const GDScriptParser::IdentifierNode *>(E->get()->arguments[1])->name);
- //printf("named key %x\n",key_idx);
+ StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- } else {
- if (prev_pos & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ GDScriptCodeGenerator::Address member = codegen.add_temporary();
+ gen->write_get_member(member, name);
+ gen->write_operator(assigned, assignment->variant_op, member, assigned);
+ gen->pop_temporary();
+ }
- GDScriptParser::Node *key = E->get()->arguments[1];
- key_idx = _parse_expression(codegen, key, slevel);
- //printf("expr key %x\n",key_idx);
+ gen->write_set_member(assigned, name);
- //stack was raised here if retval was stack but..
- }
+ if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ } else {
+ // Regular assignment.
+ GDScriptCodeGenerator::Address target;
+
+ bool has_setter = false;
+ bool is_in_setter = false;
+ StringName setter_function;
+ if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
+ StringName var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
+ if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ setter_function = codegen.script->member_indices[var_name].setter;
+ if (setter_function != StringName()) {
+ has_setter = true;
+ is_in_setter = setter_function == codegen.function_name;
+ target.mode = GDScriptCodeGenerator::Address::MEMBER;
+ target.address = codegen.script->member_indices[var_name].index;
+ }
+ }
+ }
- if (key_idx < 0) { //error
- return key_idx;
- }
+ if (has_setter) {
+ if (!is_in_setter) {
+ // Store stack slot for the temp value.
+ target = codegen.add_temporary(_gdtype_from_datatype(assignment->assignee->get_datatype()));
+ }
+ } else {
+ target = _parse_expression(codegen, r_error, assignment->assignee);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ }
- codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET);
- codegen.opcodes.push_back(prev_pos);
- codegen.opcodes.push_back(key_idx);
- slevel++;
- codegen.alloc_stack(slevel);
- int dst_pos = (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) | slevel;
+ GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ GDScriptCodeGenerator::Address op_result;
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- codegen.opcodes.push_back(dst_pos);
+ if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ // Perform operation.
+ op_result = codegen.add_temporary();
+ gen->write_operator(op_result, assignment->variant_op, target, assigned);
+ } else {
+ op_result = assigned;
+ assigned = GDScriptCodeGenerator::Address();
+ }
- //add in reverse order, since it will be reverted
+ GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype());
- setchain.push_back(dst_pos);
- setchain.push_back(key_idx);
- setchain.push_back(prev_pos);
- setchain.push_back(named ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
+ if (has_setter && !is_in_setter) {
+ // Call setter.
+ Vector<GDScriptCodeGenerator::Address> args;
+ args.push_back(op_result);
+ gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args);
+ } else {
+ // Just assign.
+ gen->write_assign(target, op_result);
+ }
- prev_pos = dst_pos;
- }
+ if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ if (target.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ }
+ return GDScriptCodeGenerator::Address(); // Assignment does not return a value.
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code.
+ } break;
+ }
+}
- setchain.invert();
+GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested) {
+ switch (p_pattern->pattern_type) {
+ case GDScriptParser::PatternNode::PT_LITERAL: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
- int set_index;
- bool named = false;
+ // Get literal type into constant map.
+ GDScriptCodeGenerator::Address literal_type_addr = codegen.add_constant((int)p_pattern->literal->value.get_type());
- if (op->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
- set_index = codegen.get_name_map_pos(static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name);
- named = true;
- } else {
- set_index = _parse_expression(codegen, op->arguments[1], slevel + 1);
- named = false;
- }
+ // Equality is always a boolean.
+ GDScriptDataType equality_type;
+ equality_type.has_type = true;
+ equality_type.kind = GDScriptDataType::BUILTIN;
+ equality_type.builtin_type = Variant::BOOL;
- if (set_index < 0) { //error
- return set_index;
- }
+ // Check type equality.
+ GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type);
+ codegen.generator->write_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
+ codegen.generator->write_and_left_operand(type_equality_addr);
- if (set_index & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ // Get literal.
+ GDScriptCodeGenerator::Address literal_addr = _parse_expression(codegen, r_error, p_pattern->literal);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- int set_value = _parse_assign_right_expression(codegen, on, slevel + 1, named ? 0 : set_index);
- if (set_value < 0) { //error
- return set_value;
- }
+ // Check value equality.
+ GDScriptCodeGenerator::Address equality_addr = codegen.add_temporary(equality_type);
+ codegen.generator->write_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
+ codegen.generator->write_and_right_operand(equality_addr);
- codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
- codegen.opcodes.push_back(prev_pos);
- codegen.opcodes.push_back(set_index);
- codegen.opcodes.push_back(set_value);
+ // AND both together (reuse temporary location).
+ codegen.generator->write_end_and(type_equality_addr);
- for (int i = 0; i < setchain.size(); i++) {
- codegen.opcodes.push_back(setchain[i]);
- }
+ codegen.generator->pop_temporary(); // Remove equality_addr from stack.
- return retval;
+ if (literal_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
- } else if (on->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(on->arguments[0])->name)) {
- //assignment to member property
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(type_equality_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(type_equality_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, type_equality_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove type_equality_addr.
- int slevel = p_stack_level;
+ return p_previous_test;
+ } break;
+ case GDScriptParser::PatternNode::PT_EXPRESSION: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ // Create the result temps first since it's the last to go away.
+ GDScriptCodeGenerator::Address result_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address equality_test_addr = codegen.add_temporary();
+
+ // Evaluate expression.
+ GDScriptCodeGenerator::Address expr_addr;
+ expr_addr = _parse_expression(codegen, r_error, p_pattern->expression);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- int src_address = _parse_assign_right_expression(codegen, on, slevel);
- if (src_address < 0) {
- return -1;
- }
+ // Evaluate expression type.
+ Vector<GDScriptCodeGenerator::Address> typeof_args;
+ typeof_args.push_back(expr_addr);
+ codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args);
- StringName name = static_cast<GDScriptParser::IdentifierNode *>(on->arguments[0])->name;
+ // Check type equality.
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
+ codegen.generator->write_and_left_operand(result_addr);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
- codegen.opcodes.push_back(codegen.get_name_map_pos(name));
- codegen.opcodes.push_back(src_address);
+ // Check value equality.
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
+ codegen.generator->write_and_right_operand(equality_test_addr);
- return GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
- } else {
- //REGULAR ASSIGNMENT MODE!!
+ // AND both type and value equality.
+ codegen.generator->write_end_and(result_addr);
- int slevel = p_stack_level;
+ // We don't need the expression temporary anymore.
+ if (expr_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+ codegen.generator->pop_temporary(); // Remove type equality temporary.
+
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
- int dst_address_a = _parse_expression(codegen, on->arguments[0], slevel, false, on->op == GDScriptParser::OperatorNode::OP_INIT_ASSIGN);
- if (dst_address_a < 0) {
- return -1;
- }
+ return p_previous_test;
+ } break;
+ case GDScriptParser::PatternNode::PT_ARRAY: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ // Get array type into constant map.
+ GDScriptCodeGenerator::Address array_type_addr = codegen.add_constant((int)Variant::ARRAY);
+
+ // Equality is always a boolean.
+ GDScriptDataType temp_type;
+ temp_type.has_type = true;
+ temp_type.kind = GDScriptDataType::BUILTIN;
+ temp_type.builtin_type = Variant::BOOL;
+
+ // Check type equality.
+ GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
+ codegen.generator->write_and_left_operand(result_addr);
+
+ // Store pattern length in constant map.
+ GDScriptCodeGenerator::Address array_length_addr = codegen.add_constant(p_pattern->rest_used ? p_pattern->array.size() - 1 : p_pattern->array.size());
+
+ // Get value length.
+ temp_type.builtin_type = Variant::INT;
+ GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
+ Vector<GDScriptCodeGenerator::Address> len_args;
+ len_args.push_back(p_value_addr);
+ codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args);
+
+ // Test length compatibility.
+ temp_type.builtin_type = Variant::BOOL;
+ GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
+ codegen.generator->write_and_right_operand(length_compat_addr);
+
+ // AND type and length check.
+ codegen.generator->write_end_and(result_addr);
+
+ // Remove length temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ // Create temporaries outside the loop so they can be reused.
+ GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address test_addr = p_previous_test;
+
+ // Evaluate element by element.
+ for (int i = 0; i < p_pattern->array.size(); i++) {
+ if (p_pattern->array[i]->pattern_type == GDScriptParser::PatternNode::PT_REST) {
+ // Don't want to access an extra element of the user array.
+ break;
+ }
- if (dst_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
+ codegen.generator->write_and_left_operand(test_addr);
- int src_address_b = _parse_assign_right_expression(codegen, on, slevel);
- if (src_address_b < 0) {
- return -1;
- }
+ // Add index to constant map.
+ GDScriptCodeGenerator::Address index_addr = codegen.add_constant(i);
- GDScriptDataType assign_type = _gdtype_from_datatype(on->arguments[0]->get_datatype());
-
- if (assign_type.has_type && !on->datatype.has_type) {
- // Typed assignment
- switch (assign_type.kind) {
- case GDScriptDataType::BUILTIN: {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
- codegen.opcodes.push_back(assign_type.builtin_type); // variable type
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx;
- if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
- class_idx = GDScriptLanguage::get_singleton()->get_global_map()[assign_type.native_type];
- class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
- } else {
- _set_error("Invalid native class type '" + String(assign_type.native_type) + "'.", on->arguments[0]);
- return -1;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
- codegen.opcodes.push_back(class_idx); // variable type
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = assign_type.script_type;
- int idx = codegen.get_constant_pos(script);
- idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
- codegen.opcodes.push_back(idx); // variable type
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2
- } break;
- default: {
- ERR_PRINT("Compiler bug: unresolved assign.");
-
- // Shouldn't get here, but fail-safe to a regular assignment
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
- }
- }
- } else {
- // Either untyped assignment or already type-checked by the parser
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
- }
- return dst_address_a; //if anything, returns wathever was assigned or correct stack position
- }
- } break;
- case GDScriptParser::OperatorNode::OP_IS: {
- ERR_FAIL_COND_V(on->arguments.size() != 2, false);
+ // Get the actual element from the user-sent array.
+ codegen.generator->write_get(element_addr, index_addr, p_value_addr);
- int slevel = p_stack_level;
+ // Also get type of element.
+ Vector<GDScriptCodeGenerator::Address> typeof_args;
+ typeof_args.push_back(element_addr);
+ codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args);
- int src_address_a = _parse_expression(codegen, on->arguments[0], slevel);
- if (src_address_a < 0) {
- return -1;
- }
+ // Try the pattern inside the element.
+ test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
+ if (r_error != OK) {
+ return GDScriptCodeGenerator::Address();
+ }
- if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++; //uses stack for return, increase stack
- }
+ codegen.generator->write_and_right_operand(test_addr);
+ codegen.generator->write_end_and(test_addr);
+ }
+ // Remove element temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
- int src_address_b = _parse_expression(codegen, on->arguments[1], slevel);
- if (src_address_b < 0) {
- return -1;
- }
+ return test_addr;
+ } break;
+ case GDScriptParser::PatternNode::PT_DICTIONARY: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ // Get dictionary type into constant map.
+ GDScriptCodeGenerator::Address dict_type_addr = codegen.add_constant((int)Variant::DICTIONARY);
+
+ // Equality is always a boolean.
+ GDScriptDataType temp_type;
+ temp_type.has_type = true;
+ temp_type.kind = GDScriptDataType::BUILTIN;
+ temp_type.builtin_type = Variant::BOOL;
+
+ // Check type equality.
+ GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
+ codegen.generator->write_and_left_operand(result_addr);
+
+ // Store pattern length in constant map.
+ GDScriptCodeGenerator::Address dict_length_addr = codegen.add_constant(p_pattern->rest_used ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());
+
+ // Get user's dictionary length.
+ temp_type.builtin_type = Variant::INT;
+ GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
+ Vector<GDScriptCodeGenerator::Address> func_args;
+ func_args.push_back(p_value_addr);
+ codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args);
+
+ // Test length compatibility.
+ temp_type.builtin_type = Variant::BOOL;
+ GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
+ codegen.generator->write_and_right_operand(length_compat_addr);
+
+ // AND type and length check.
+ codegen.generator->write_end_and(result_addr);
+
+ // Remove length temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ // Create temporaries outside the loop so they can be reused.
+ temp_type.builtin_type = Variant::BOOL;
+ GDScriptCodeGenerator::Address test_result = codegen.add_temporary(temp_type);
+ GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address test_addr = p_previous_test;
+
+ // Evaluate element by element.
+ for (int i = 0; i < p_pattern->dictionary.size(); i++) {
+ const GDScriptParser::PatternNode::Pair &element = p_pattern->dictionary[i];
+ if (element.value_pattern && element.value_pattern->pattern_type == GDScriptParser::PatternNode::PT_REST) {
+ // Ignore rest pattern.
+ break;
+ }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_EXTENDS_TEST); // perform operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
+ codegen.generator->write_and_left_operand(test_addr);
- } break;
- case GDScriptParser::OperatorNode::OP_IS_BUILTIN: {
- ERR_FAIL_COND_V(on->arguments.size() != 2, false);
- ERR_FAIL_COND_V(on->arguments[1]->type != GDScriptParser::Node::TYPE_TYPE, false);
+ // Get the pattern key.
+ GDScriptCodeGenerator::Address pattern_key_addr = _parse_expression(codegen, r_error, element.key);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
- int slevel = p_stack_level;
+ // Check if pattern key exists in user's dictionary. This will be AND-ed with next result.
+ func_args.clear();
+ func_args.push_back(pattern_key_addr);
+ codegen.generator->write_call(test_result, p_value_addr, "has", func_args);
- int src_address_a = _parse_expression(codegen, on->arguments[0], slevel);
- if (src_address_a < 0) {
- return -1;
- }
+ if (element.value_pattern != nullptr) {
+ // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
+ codegen.generator->write_and_left_operand(test_result);
- if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++; //uses stack for return, increase stack
- }
+ // Get actual value from user dictionary.
+ codegen.generator->write_get(element_addr, pattern_key_addr, p_value_addr);
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(on->arguments[1]);
+ // Also get type of value.
+ func_args.clear();
+ func_args.push_back(element_addr);
+ codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_IS_BUILTIN); // perform operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back((int)tn->vtype); // argument 2 (unary only takes one parameter)
- } break;
- default: {
- ERR_FAIL_V_MSG(0, "Bug in bytecode compiler, unexpected operator #" + itos(on->op) + " in parse tree while parsing expression."); //unreachable code
+ // Try the pattern inside the value.
+ test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
+ if (r_error != OK) {
+ return GDScriptCodeGenerator::Address();
+ }
+ codegen.generator->write_and_right_operand(test_addr);
+ codegen.generator->write_end_and(test_addr);
+ }
- } break;
+ codegen.generator->write_and_right_operand(test_addr);
+ codegen.generator->write_end_and(test_addr);
+
+ // Remove pattern key temporary.
+ if (pattern_key_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
}
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
- } break;
- //TYPE_TYPE,
- default: {
- ERR_FAIL_V_MSG(-1, "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); //unreachable code
+ // Remove element temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+
+ return test_addr;
} break;
+ case GDScriptParser::PatternNode::PT_REST:
+ // Do nothing.
+ return p_previous_test;
+ break;
+ case GDScriptParser::PatternNode::PT_BIND: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ // Get the bind address.
+ GDScriptCodeGenerator::Address bind = codegen.locals[p_pattern->bind->name];
+
+ // Assign value to bound variable.
+ codegen.generator->write_assign(bind, p_value_addr);
+ }
+ [[fallthrough]]; // Act like matching anything too.
+ case GDScriptParser::PatternNode::PT_WILDCARD:
+ // If this is a fall through we don't want to do this again.
+ if (p_pattern->pattern_type != GDScriptParser::PatternNode::PT_BIND) {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ }
+ // This matches anything so just do the same as `if(true)`.
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the operator with the `true` constant so it works as always matching.
+ GDScriptCodeGenerator::Address constant = codegen.add_constant(true);
+ codegen.generator->write_and_right_operand(constant);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the operator with the `true` constant so it works as always matching.
+ GDScriptCodeGenerator::Address constant = codegen.add_constant(true);
+ codegen.generator->write_or_right_operand(constant);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign_true(p_previous_test);
+ }
+ return p_previous_test;
}
+ ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern.");
}
-Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level, int p_break_addr, int p_continue_addr) {
- codegen.push_stack_identifiers();
- int new_identifiers = 0;
- codegen.current_line = p_block->line;
+void GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) {
+ for (int i = 0; i < p_block->locals.size(); i++) {
+ if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) {
+ // Parameters are added directly from function and loop variables are declared explicitly.
+ continue;
+ }
+ codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype()));
+ }
+}
+
+Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals) {
+ Error error = OK;
+ GDScriptCodeGenerator *gen = codegen.generator;
+
+ codegen.start_block();
+
+ if (p_add_locals) {
+ _add_locals_in_block(codegen, p_block);
+ }
for (int i = 0; i < p_block->statements.size(); i++) {
const GDScriptParser::Node *s = p_block->statements[i];
- switch (s->type) {
- case GDScriptParser::Node::TYPE_NEWLINE: {
#ifdef DEBUG_ENABLED
- const GDScriptParser::NewLineNode *nl = static_cast<const GDScriptParser::NewLineNode *>(s);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
- codegen.opcodes.push_back(nl->line);
- codegen.current_line = nl->line;
+ // Add a newline before each statement, since the debugger needs those.
+ gen->write_newline(s->start_line);
#endif
- } break;
- case GDScriptParser::Node::TYPE_CONTROL_FLOW: {
- // try subblocks
- const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(s);
+ switch (s->type) {
+ case GDScriptParser::Node::MATCH: {
+ const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s);
+
+ gen->start_match();
+ codegen.start_block();
- switch (cf->cf_type) {
- case GDScriptParser::ControlFlowNode::CF_MATCH: {
- GDScriptParser::MatchNode *match = cf->match;
+ // Evaluate the match expression.
+ GDScriptCodeGenerator::Address value = _parse_expression(codegen, error, match->test);
+ if (error) {
+ return error;
+ }
- GDScriptParser::IdentifierNode *id = memnew(GDScriptParser::IdentifierNode);
- id->name = "#match_value";
+ // Then, let's save the type of the value in the stack too, so we can reuse for later comparisons.
+ GDScriptCodeGenerator::Address type = codegen.add_temporary();
+ Vector<GDScriptCodeGenerator::Address> typeof_args;
+ typeof_args.push_back(value);
+ gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args);
+
+ // Now we can actually start testing.
+ // For each branch.
+ for (int j = 0; j < match->branches.size(); j++) {
+ if (j > 0) {
+ // Use `else` to not check the next branch after matching.
+ gen->write_else();
+ }
- // var #match_value
- // copied because there is no _parse_statement :(
- codegen.add_stack_identifier(id->name, p_stack_level++);
- codegen.alloc_stack(p_stack_level);
- new_identifiers++;
+ const GDScriptParser::MatchBranchNode *branch = match->branches[j];
- GDScriptParser::OperatorNode *op = memnew(GDScriptParser::OperatorNode);
- op->op = GDScriptParser::OperatorNode::OP_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(match->val_to_match);
+ gen->start_match_branch(); // Need so lower level code can patch 'continue' jumps.
+ codegen.start_block(); // Create an extra block around for binds.
- int ret = _parse_expression(codegen, op, p_stack_level);
- if (ret < 0) {
- memdelete(id);
- memdelete(op);
- return ERR_PARSE_ERROR;
+ // Add locals in block before patterns, so temporaries don't use the stack address for binds.
+ _add_locals_in_block(codegen, branch->block);
+
+#ifdef DEBUG_ENABLED
+ // Add a newline before each branch, since the debugger needs those.
+ gen->write_newline(branch->start_line);
+#endif
+ // For each pattern in branch.
+ GDScriptCodeGenerator::Address pattern_result = codegen.add_temporary();
+ for (int k = 0; k < branch->patterns.size(); k++) {
+ pattern_result = _parse_match_pattern(codegen, error, branch->patterns[k], value, type, pattern_result, k == 0, false);
+ if (error != OK) {
+ return error;
}
+ }
- // break address
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- int break_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(0); // break addr
-
- for (int j = 0; j < match->compiled_pattern_branches.size(); j++) {
- GDScriptParser::MatchNode::CompiledPatternBranch branch = match->compiled_pattern_branches[j];
-
- // jump over continue
- // jump unconditionally
- // continue address
- // compile the condition
- int ret2 = _parse_expression(codegen, branch.compiled_pattern, p_stack_level);
- if (ret2 < 0) {
- memdelete(id);
- memdelete(op);
- return ERR_PARSE_ERROR;
- }
+ // Check if pattern did match.
+ gen->write_if(pattern_result);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF);
- codegen.opcodes.push_back(ret2);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- int continue_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(0);
-
- Error err = _parse_block(codegen, branch.body, p_stack_level, p_break_addr, continue_addr);
- if (err) {
- memdelete(id);
- memdelete(op);
- return ERR_PARSE_ERROR;
- }
+ // Remove the result from stack.
+ gen->pop_temporary();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(break_addr);
+ // Parse the branch block.
+ error = _parse_block(codegen, branch->block, false); // Don't add locals again.
+ if (error) {
+ return error;
+ }
- codegen.opcodes.write[continue_addr + 1] = codegen.opcodes.size();
- }
+ codegen.end_block(); // Get out of extra block.
+ }
- codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
+ // End all nested `if`s.
+ for (int j = 0; j < match->branches.size(); j++) {
+ gen->write_endif();
+ }
- memdelete(id);
- memdelete(op);
+ gen->pop_temporary();
- } break;
+ if (value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
- case GDScriptParser::ControlFlowNode::CF_IF: {
- int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
- }
+ gen->end_match();
+ } break;
+ case GDScriptParser::Node::IF: {
+ const GDScriptParser::IfNode *if_n = static_cast<const GDScriptParser::IfNode *>(s);
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, if_n->condition);
+ if (error) {
+ return error;
+ }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(ret2);
- int else_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(0); //temporary
+ gen->write_if(condition);
- Error err = _parse_block(codegen, cf->body, p_stack_level, p_break_addr, p_continue_addr);
- if (err) {
- return err;
- }
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
- if (cf->body_else) {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- int end_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
- codegen.opcodes.write[else_addr] = codegen.opcodes.size();
+ error = _parse_block(codegen, if_n->true_block);
+ if (error) {
+ return error;
+ }
- Error err2 = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr);
- if (err2) {
- return err2;
- }
+ if (if_n->false_block) {
+ gen->write_else();
- codegen.opcodes.write[end_addr] = codegen.opcodes.size();
- } else {
- //end without else
- codegen.opcodes.write[else_addr] = codegen.opcodes.size();
- }
+ error = _parse_block(codegen, if_n->false_block);
+ if (error) {
+ return error;
+ }
+ }
- } break;
- case GDScriptParser::ControlFlowNode::CF_FOR: {
- int slevel = p_stack_level;
- int iter_stack_pos = slevel;
- int iterator_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int counter_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int container_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.alloc_stack(slevel);
-
- codegen.push_stack_identifiers();
- codegen.add_stack_identifier(static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0])->name, iter_stack_pos);
-
- int ret2 = _parse_expression(codegen, cf->arguments[1], slevel, false);
- if (ret2 < 0) {
- return ERR_COMPILATION_FAILED;
- }
+ gen->write_endif();
+ } break;
+ case GDScriptParser::Node::FOR: {
+ const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s);
- //assign container
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(ret2);
-
- //begin loop
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE_BEGIN);
- codegen.opcodes.push_back(counter_pos);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(codegen.opcodes.size() + 4);
- codegen.opcodes.push_back(iterator_pos);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
- codegen.opcodes.push_back(codegen.opcodes.size() + 8);
- //break loop
- int break_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
- codegen.opcodes.push_back(0); //skip code for next
- //next loop
- int continue_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE);
- codegen.opcodes.push_back(counter_pos);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(break_pos);
- codegen.opcodes.push_back(iterator_pos);
-
- Error err = _parse_block(codegen, cf->body, slevel, break_pos, continue_pos);
- if (err) {
- return err;
- }
+ codegen.start_block();
+ GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype()));
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(continue_pos);
- codegen.opcodes.write[break_pos + 1] = codegen.opcodes.size();
+ GDScriptCodeGenerator::Address list = _parse_expression(codegen, error, for_n->list);
+ if (error) {
+ return error;
+ }
- codegen.pop_stack_identifiers();
+ gen->write_for(iterator, list);
- } break;
- case GDScriptParser::ControlFlowNode::CF_WHILE: {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- int break_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(0);
- int continue_addr = codegen.opcodes.size();
+ error = _parse_block(codegen, for_n->loop);
+ if (error) {
+ return error;
+ }
- int ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(ret2);
- codegen.opcodes.push_back(break_addr);
- Error err = _parse_block(codegen, cf->body, p_stack_level, break_addr, continue_addr);
- if (err) {
- return err;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(continue_addr);
+ gen->write_endfor();
+
+ if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
- codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
+ codegen.end_block();
+ } break;
+ case GDScriptParser::Node::WHILE: {
+ const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s);
- } break;
- case GDScriptParser::ControlFlowNode::CF_BREAK: {
- if (p_break_addr < 0) {
- _set_error("'break'' not within loop", cf);
- return ERR_COMPILATION_FAILED;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(p_break_addr);
-
- } break;
- case GDScriptParser::ControlFlowNode::CF_CONTINUE: {
- if (p_continue_addr < 0) {
- _set_error("'continue' not within loop", cf);
- return ERR_COMPILATION_FAILED;
- }
+ gen->start_while_condition();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(p_continue_addr);
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, while_n->condition);
+ if (error) {
+ return error;
+ }
- } break;
- case GDScriptParser::ControlFlowNode::CF_RETURN: {
- int ret2;
+ gen->write_while(condition);
- if (cf->arguments.size()) {
- ret2 = _parse_expression(codegen, cf->arguments[0], p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
- }
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
- } else {
- ret2 = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
- }
+ error = _parse_block(codegen, while_n->loop);
+ if (error) {
+ return error;
+ }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_RETURN);
- codegen.opcodes.push_back(ret2);
+ gen->write_endwhile();
+ } break;
+ case GDScriptParser::Node::BREAK: {
+ gen->write_break();
+ } break;
+ case GDScriptParser::Node::CONTINUE: {
+ const GDScriptParser::ContinueNode *cont = static_cast<const GDScriptParser::ContinueNode *>(s);
+ if (cont->is_for_match) {
+ gen->write_continue_match();
+ } else {
+ gen->write_continue();
+ }
+ } break;
+ case GDScriptParser::Node::RETURN: {
+ const GDScriptParser::ReturnNode *return_n = static_cast<const GDScriptParser::ReturnNode *>(s);
+
+ GDScriptCodeGenerator::Address return_value;
- } break;
+ if (return_n->return_value != nullptr) {
+ return_value = _parse_expression(codegen, error, return_n->return_value);
+ if (error) {
+ return error;
+ }
+ }
+
+ gen->write_return(return_value);
+ if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
}
} break;
- case GDScriptParser::Node::TYPE_ASSERT: {
+ case GDScriptParser::Node::ASSERT: {
#ifdef DEBUG_ENABLED
- // try subblocks
-
const GDScriptParser::AssertNode *as = static_cast<const GDScriptParser::AssertNode *>(s);
- int ret2 = _parse_expression(codegen, as->condition, p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, as->condition);
+ if (error) {
+ return error;
}
- int message_ret = 0;
+ GDScriptCodeGenerator::Address message;
+
if (as->message) {
- message_ret = _parse_expression(codegen, as->message, p_stack_level + 1, false);
- if (message_ret < 0) {
- return ERR_PARSE_ERROR;
+ message = _parse_expression(codegen, error, as->message);
+ if (error) {
+ return error;
}
}
+ gen->write_assert(condition, message);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSERT);
- codegen.opcodes.push_back(ret2);
- codegen.opcodes.push_back(message_ret);
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+ if (message.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
#endif
} break;
- case GDScriptParser::Node::TYPE_BREAKPOINT: {
+ case GDScriptParser::Node::BREAKPOINT: {
#ifdef DEBUG_ENABLED
- // try subblocks
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_BREAKPOINT);
+ gen->write_breakpoint();
#endif
} break;
- case GDScriptParser::Node::TYPE_LOCAL_VAR: {
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(s);
-
- // since we are using properties now for most class access, allow shadowing of class members to make user's life easier.
- //
- //if (_is_class_member_property(codegen, lv->name)) {
- // _set_error("Name for local variable '" + String(lv->name) + "' can't shadow class property of the same name.", lv);
- // return ERR_ALREADY_EXISTS;
- //}
-
- codegen.add_stack_identifier(lv->name, p_stack_level++);
- codegen.alloc_stack(p_stack_level);
- new_identifiers++;
+ case GDScriptParser::Node::VARIABLE: {
+ const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
+ // Should be already in stack when the block began.
+ GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
+
+ if (lv->initializer != nullptr) {
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, lv->initializer);
+ if (error) {
+ return error;
+ }
+ gen->write_assign(local, src_address);
+ if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+ }
+ } break;
+ case GDScriptParser::Node::CONSTANT: {
+ // Local constants.
+ const GDScriptParser::ConstantNode *lc = static_cast<const GDScriptParser::ConstantNode *>(s);
+ if (!lc->initializer->is_constant) {
+ _set_error("Local constant must have a constant value as initializer.", lc->initializer);
+ return ERR_PARSE_ERROR;
+ }
+ codegen.add_local_constant(lc->identifier->name, lc->initializer->reduced_value);
} break;
+ case GDScriptParser::Node::PASS:
+ // Nothing to do.
+ break;
default: {
- //expression
- int ret2 = _parse_expression(codegen, s, p_stack_level, true);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+ // Expression.
+ if (s->is_expression()) {
+ GDScriptCodeGenerator::Address expr = _parse_expression(codegen, error, static_cast<const GDScriptParser::ExpressionNode *>(s), true);
+ if (error) {
+ return error;
+ }
+ if (expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+ } else {
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Bug in bytecode compiler, unexpected node in parse tree while parsing statement."); // Unreachable code.
}
} break;
}
}
- codegen.pop_stack_identifiers();
+
+ codegen.end_block();
return OK;
}
Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
- Vector<int> bytecode;
+ Error error = OK;
CodeGen codegen;
+ codegen.generator = memnew(GDScriptByteCodeGenerator);
codegen.class_node = p_class;
codegen.script = p_script;
codegen.function_node = p_func;
- codegen.stack_max = 0;
- codegen.current_line = 0;
- codegen.call_max = 0;
- codegen.debug_stack = EngineDebugger::is_active();
- Vector<StringName> argnames;
- int stack_level = 0;
+ StringName func_name;
+ bool is_static = false;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ GDScriptDataType return_type;
+ return_type.has_type = true;
+ return_type.kind = GDScriptDataType::BUILTIN;
+ return_type.builtin_type = Variant::NIL;
if (p_func) {
- for (int i = 0; i < p_func->arguments.size(); i++) {
- // since we are using properties now for most class access, allow shadowing of class members to make user's life easier.
- //
- //if (_is_class_member_property(p_script, p_func->arguments[i])) {
- // _set_error("Name for argument '" + String(p_func->arguments[i]) + "' can't shadow class property of the same name.", p_func);
- // return ERR_ALREADY_EXISTS;
- //}
-
- codegen.add_stack_identifier(p_func->arguments[i], i);
-#ifdef TOOLS_ENABLED
- argnames.push_back(p_func->arguments[i]);
-#endif
+ func_name = p_func->identifier->name;
+ is_static = p_func->is_static;
+ rpc_mode = p_func->rpc_mode;
+ return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
+ } else {
+ if (p_for_ready) {
+ func_name = "_ready";
+ } else {
+ func_name = "@implicit_new";
}
- stack_level = p_func->arguments.size();
}
- codegen.alloc_stack(stack_level);
+ codegen.function_name = func_name;
+ codegen.generator->write_start(p_script, func_name, is_static, rpc_mode, return_type);
- /* Parse initializer -if applies- */
+ int optional_parameters = 0;
- bool is_initializer = !p_for_ready && !p_func;
-
- if (is_initializer || (p_func && String(p_func->name) == "_init")) {
- //parse initializer for class members
- if (!p_func && p_class->extends_used && p_script->native.is_null()) {
- //call implicit parent constructor
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_SELF_BASE);
- codegen.opcodes.push_back(codegen.get_name_map_pos("_init"));
- codegen.opcodes.push_back(0);
- codegen.opcodes.push_back((GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) | 0);
- }
- Error err = _parse_block(codegen, p_class->initializer, stack_level);
- if (err) {
- return err;
+ if (p_func) {
+ for (int i = 0; i < p_func->parameters.size(); i++) {
+ const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
+ GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype(), p_script);
+ uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->default_value != nullptr, par_type);
+ codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type);
+
+ if (p_func->parameters[i]->default_value != nullptr) {
+ optional_parameters++;
+ }
}
- is_initializer = true;
}
- if (p_for_ready || (p_func && String(p_func->name) == "_ready")) {
- //parse initializer for class members
- if (p_class->ready->statements.size()) {
- Error err = _parse_block(codegen, p_class->ready, stack_level);
- if (err) {
- return err;
+ // Parse initializer if applies.
+ bool is_implicit_initializer = !p_for_ready && !p_func;
+ bool is_initializer = p_func && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
+ bool is_for_ready = p_for_ready || (p_func && String(p_func->identifier->name) == "_ready");
+
+ if (is_implicit_initializer || is_for_ready) {
+ // Initialize class fields.
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
+ continue;
+ }
+ const GDScriptParser::VariableNode *field = p_class->members[i].variable;
+ if (field->onready != is_for_ready) {
+ // Only initialize in _ready.
+ continue;
}
- }
- }
- /* Parse default argument code -if applies- */
+ if (field->initializer) {
+ // Emit proper line change.
+ codegen.generator->write_newline(field->initializer->start_line);
- Vector<int> defarg_addr;
- StringName func_name;
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true);
+ if (error) {
+ memdelete(codegen.generator);
+ return error;
+ }
+ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
- if (p_func) {
- if (p_func->default_values.size()) {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
- defarg_addr.push_back(codegen.opcodes.size());
- for (int i = 0; i < p_func->default_values.size(); i++) {
- _parse_expression(codegen, p_func->default_values[i], stack_level, true);
- defarg_addr.push_back(codegen.opcodes.size());
+ codegen.generator->write_assign(dst_address, src_address);
+ if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
}
+ }
+ }
- defarg_addr.invert();
+ // Parse default argument code if applies.
+ if (p_func) {
+ if (optional_parameters > 0) {
+ codegen.generator->start_parameters();
+ for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
+ const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
+ GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, error, parameter->default_value, true);
+ if (error) {
+ memdelete(codegen.generator);
+ return error;
+ }
+ GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
+ codegen.generator->write_assign(dst_addr, src_addr);
+ if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+ }
+ codegen.generator->end_parameters();
}
- Error err = _parse_block(codegen, p_func->body, stack_level);
+ Error err = _parse_block(codegen, p_func->body);
if (err) {
+ memdelete(codegen.generator);
return err;
}
+ }
- func_name = p_func->name;
- } else {
- if (p_for_ready) {
- func_name = "_ready";
+#ifdef DEBUG_ENABLED
+ if (EngineDebugger::is_active()) {
+ String signature;
+ // Path.
+ if (p_script->get_path() != String()) {
+ signature += p_script->get_path();
+ }
+ // Location.
+ if (p_func) {
+ signature += "::" + itos(p_func->body->start_line);
} else {
- func_name = "_init";
+ signature += "::0";
}
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_END);
- /*
- if (String(p_func->name)=="") { //initializer func
- gdfunc = &p_script->initializer;
- */
- //} else { //regular func
- p_script->member_functions[func_name] = memnew(GDScriptFunction);
- GDScriptFunction *gdfunc = p_script->member_functions[func_name];
- //}
+ // Function and class.
- if (p_func) {
- gdfunc->_static = p_func->_static;
- gdfunc->rpc_mode = p_func->rpc_mode;
- gdfunc->argument_types.resize(p_func->argument_types.size());
- for (int i = 0; i < p_func->argument_types.size(); i++) {
- gdfunc->argument_types.write[i] = _gdtype_from_datatype(p_func->argument_types[i]);
+ if (p_class->identifier) {
+ signature += "::" + String(p_class->identifier->name) + "." + String(func_name);
+ } else {
+ signature += "::" + String(func_name);
}
- gdfunc->return_type = _gdtype_from_datatype(p_func->return_type);
- } else {
- gdfunc->_static = false;
- gdfunc->rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
- gdfunc->return_type = GDScriptDataType();
- gdfunc->return_type.has_type = true;
- gdfunc->return_type.kind = GDScriptDataType::BUILTIN;
- gdfunc->return_type.builtin_type = Variant::NIL;
+
+ codegen.generator->set_signature(signature);
}
+#endif
+ if (p_func) {
+ codegen.generator->set_initial_line(p_func->start_line);
#ifdef TOOLS_ENABLED
- gdfunc->arg_names = argnames;
+ p_script->member_lines[func_name] = p_func->start_line;
#endif
- //constants
- if (codegen.constant_map.size()) {
- gdfunc->_constant_count = codegen.constant_map.size();
- gdfunc->constants.resize(codegen.constant_map.size());
- gdfunc->_constants_ptr = gdfunc->constants.ptrw();
- const Variant *K = nullptr;
- while ((K = codegen.constant_map.next(K))) {
- int idx = codegen.constant_map[*K];
- gdfunc->constants.write[idx] = *K;
- }
} else {
- gdfunc->_constants_ptr = nullptr;
- gdfunc->_constant_count = 0;
+ codegen.generator->set_initial_line(0);
}
- //global names
- if (codegen.name_map.size()) {
- gdfunc->global_names.resize(codegen.name_map.size());
- gdfunc->_global_names_ptr = &gdfunc->global_names[0];
- for (Map<StringName, int>::Element *E = codegen.name_map.front(); E; E = E->next()) {
- gdfunc->global_names.write[E->get()] = E->key();
- }
- gdfunc->_global_names_count = gdfunc->global_names.size();
- } else {
- gdfunc->_global_names_ptr = nullptr;
- gdfunc->_global_names_count = 0;
- }
+ GDScriptFunction *gd_function = codegen.generator->write_end();
-#ifdef TOOLS_ENABLED
- // Named globals
- if (codegen.named_globals.size()) {
- gdfunc->named_globals.resize(codegen.named_globals.size());
- gdfunc->_named_globals_ptr = gdfunc->named_globals.ptr();
- for (int i = 0; i < codegen.named_globals.size(); i++) {
- gdfunc->named_globals.write[i] = codegen.named_globals[i];
- }
- gdfunc->_named_globals_count = gdfunc->named_globals.size();
+ if (is_initializer) {
+ p_script->initializer = gd_function;
+ } else if (is_implicit_initializer) {
+ p_script->implicit_initializer = gd_function;
}
-#endif
- if (codegen.opcodes.size()) {
- gdfunc->code = codegen.opcodes;
- gdfunc->_code_ptr = &gdfunc->code[0];
- gdfunc->_code_size = codegen.opcodes.size();
+ p_script->member_functions[func_name] = gd_function;
+ memdelete(codegen.generator);
+
+ return OK;
+}
+
+Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
+ Error error = OK;
+ CodeGen codegen;
+ codegen.generator = memnew(GDScriptByteCodeGenerator);
+
+ codegen.class_node = p_class;
+ codegen.script = p_script;
+
+ StringName func_name;
+
+ if (p_is_setter) {
+ func_name = "@" + p_variable->identifier->name + "_setter";
} else {
- gdfunc->_code_ptr = nullptr;
- gdfunc->_code_size = 0;
+ func_name = "@" + p_variable->identifier->name + "_getter";
}
- if (defarg_addr.size()) {
- gdfunc->default_arguments = defarg_addr;
- gdfunc->_default_arg_count = defarg_addr.size() - 1;
- gdfunc->_default_arg_ptr = &gdfunc->default_arguments[0];
+ GDScriptDataType return_type;
+ if (p_is_setter) {
+ return_type.has_type = true;
+ return_type.kind = GDScriptDataType::BUILTIN;
+ return_type.builtin_type = Variant::NIL;
} else {
- gdfunc->_default_arg_count = 0;
- gdfunc->_default_arg_ptr = nullptr;
+ return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script);
+ }
+
+ codegen.generator->write_start(p_script, func_name, false, p_variable->rpc_mode, return_type);
+
+ if (p_is_setter) {
+ uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype()));
+ codegen.parameters[p_variable->setter_parameter->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, _gdtype_from_datatype(p_variable->get_datatype()));
+ }
+
+ error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter);
+ if (error) {
+ memdelete(codegen.generator);
+ return error;
}
- gdfunc->_argument_count = p_func ? p_func->arguments.size() : 0;
- gdfunc->_stack_size = codegen.stack_max;
- gdfunc->_call_size = codegen.call_max;
- gdfunc->name = func_name;
+ GDScriptFunction *gd_function = codegen.generator->write_end();
+
+ p_script->member_functions[func_name] = gd_function;
+
#ifdef DEBUG_ENABLED
if (EngineDebugger::is_active()) {
String signature;
@@ -1805,51 +1864,25 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
signature += p_script->get_path();
}
//loc
- if (p_func) {
- signature += "::" + itos(p_func->body->line);
- } else {
- signature += "::0";
- }
+ signature += "::" + itos(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line);
//function and class
- if (p_class->name) {
- signature += "::" + String(p_class->name) + "." + String(func_name);
+ if (p_class->identifier) {
+ signature += "::" + String(p_class->identifier->name) + "." + String(func_name);
} else {
signature += "::" + String(func_name);
}
- gdfunc->profile.signature = signature;
+ codegen.generator->set_signature(signature);
}
#endif
- gdfunc->_script = p_script;
- gdfunc->source = source;
-
-#ifdef DEBUG_ENABLED
+ codegen.generator->set_initial_line(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line);
- {
- gdfunc->func_cname = (String(source) + " - " + String(func_name)).utf8();
- gdfunc->_func_cname = gdfunc->func_cname.get_data();
- }
-
-#endif
- if (p_func) {
- gdfunc->_initial_line = p_func->line;
#ifdef TOOLS_ENABLED
-
- p_script->member_lines[func_name] = p_func->line;
+ p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line;
#endif
- } else {
- gdfunc->_initial_line = 0;
- }
-
- if (codegen.debug_stack) {
- gdfunc->stack_debug = codegen.stack_debug;
- }
-
- if (is_initializer) {
- p_script->initializer = gdfunc;
- }
+ memdelete(codegen.generator);
return OK;
}
@@ -1857,14 +1890,14 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
parsing_classes.insert(p_script);
- if (p_class->owner && p_class->owner->owner) {
+ if (p_class->outer && p_class->outer->outer) {
// Owner is not root
if (!parsed_classes.has(p_script->_owner)) {
if (parsing_classes.has(p_script->_owner)) {
- _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class);
+ _set_error("Cyclic class reference for '" + String(p_class->identifier->name) + "'.", p_class);
return ERR_PARSE_ERROR;
}
- Error err = _parse_class_level(p_script->_owner, p_class->owner, p_keep_state);
+ Error err = _parse_class_level(p_script->_owner, p_class->outer, p_keep_state);
if (err) {
return err;
}
@@ -1885,8 +1918,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->_signals.clear();
p_script->initializer = nullptr;
- p_script->tool = p_class->tool;
- p_script->name = p_class->name;
+ p_script->tool = parser->is_tool();
+ p_script->name = p_class->identifier ? p_class->identifier->name : "";
Ref<GDScriptNativeClass> native;
@@ -1900,23 +1933,36 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->native = native;
} break;
case GDScriptDataType::GDSCRIPT: {
- Ref<GDScript> base = base_type.script_type;
+ Ref<GDScript> base = Ref<GDScript>(base_type.script_type);
p_script->base = base;
p_script->_base = base.ptr();
- p_script->member_indices = base->member_indices;
- if (p_class->base_type.kind == GDScriptParser::DataType::CLASS) {
- if (!parsed_classes.has(p_script->_base)) {
- if (parsing_classes.has(p_script->_base)) {
- _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class);
- return ERR_PARSE_ERROR;
+ if (p_class->base_type.kind == GDScriptParser::DataType::CLASS && p_class->base_type.class_type != nullptr) {
+ if (p_class->base_type.script_path == main_script->path) {
+ if (!parsed_classes.has(p_script->_base)) {
+ if (parsing_classes.has(p_script->_base)) {
+ String class_name = p_class->identifier ? p_class->identifier->name : "<main>";
+ _set_error("Cyclic class reference for '" + class_name + "'.", p_class);
+ return ERR_PARSE_ERROR;
+ }
+ Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ return err;
+ }
}
- Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state);
+ } else {
+ Error err = OK;
+ base = GDScriptCache::get_full_script(p_class->base_type.script_path, err, main_script->path);
if (err) {
return err;
}
+ if (base.is_null() && !base->is_valid()) {
+ return ERR_COMPILATION_FAILED;
+ }
}
}
+
+ p_script->member_indices = base->member_indices;
} break;
default: {
_set_error("Parser bug: invalid inheritance.", p_class);
@@ -1924,86 +1970,142 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
} break;
}
- for (int i = 0; i < p_class->variables.size(); i++) {
- StringName name = p_class->variables[i].identifier;
-
- GDScript::MemberInfo minfo;
- minfo.index = p_script->member_indices.size();
- minfo.setter = p_class->variables[i].setter;
- minfo.getter = p_class->variables[i].getter;
- minfo.rpc_mode = p_class->variables[i].rpc_mode;
- minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = p_class->members[i];
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ const GDScriptParser::VariableNode *variable = member.variable;
+ StringName name = variable->identifier->name;
+
+ GDScript::MemberInfo minfo;
+ minfo.index = p_script->member_indices.size();
+ switch (variable->property) {
+ case GDScriptParser::VariableNode::PROP_NONE:
+ break; // Nothing to do.
+ case GDScriptParser::VariableNode::PROP_SETGET:
+ if (variable->setter_pointer != nullptr) {
+ minfo.setter = variable->setter_pointer->name;
+ }
+ if (variable->getter_pointer != nullptr) {
+ minfo.getter = variable->getter_pointer->name;
+ }
+ break;
+ case GDScriptParser::VariableNode::PROP_INLINE:
+ if (variable->setter != nullptr) {
+ minfo.setter = "@" + variable->identifier->name + "_setter";
+ }
+ if (variable->getter != nullptr) {
+ minfo.getter = "@" + variable->identifier->name + "_getter";
+ }
+ break;
+ }
+ minfo.rpc_mode = variable->rpc_mode;
+ minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script);
- PropertyInfo prop_info = minfo.data_type;
- prop_info.name = name;
- PropertyInfo export_info = p_class->variables[i]._export;
+ PropertyInfo prop_info = minfo.data_type;
+ prop_info.name = name;
+ PropertyInfo export_info = variable->export_info;
- if (export_info.type != Variant::NIL) {
- if (!minfo.data_type.has_type) {
- prop_info.type = export_info.type;
- prop_info.class_name = export_info.class_name;
- }
- prop_info.hint = export_info.hint;
- prop_info.hint_string = export_info.hint_string;
- prop_info.usage = export_info.usage;
+ if (variable->exported) {
+ if (!minfo.data_type.has_type) {
+ prop_info.type = export_info.type;
+ prop_info.class_name = export_info.class_name;
+ }
+ prop_info.hint = export_info.hint;
+ prop_info.hint_string = export_info.hint_string;
+ prop_info.usage = export_info.usage;
#ifdef TOOLS_ENABLED
- if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
- p_script->member_default_values[name] = p_class->variables[i].default_value;
- }
+ if (variable->initializer != nullptr && variable->initializer->type == GDScriptParser::Node::LITERAL) {
+ p_script->member_default_values[name] = static_cast<const GDScriptParser::LiteralNode *>(variable->initializer)->value;
+ }
#endif
- } else {
- prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
- }
+ } else {
+ prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
+ }
- p_script->member_info[name] = prop_info;
- p_script->member_indices[name] = minfo;
- p_script->members.insert(name);
+ p_script->member_info[name] = prop_info;
+ p_script->member_indices[name] = minfo;
+ p_script->members.insert(name);
#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = p_class->variables[i].line;
+ p_script->member_lines[name] = variable->start_line;
#endif
- }
+ } break;
+
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ const GDScriptParser::ConstantNode *constant = member.constant;
+ StringName name = constant->identifier->name;
- for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
- StringName name = E->key();
+ p_script->constants.insert(name, constant->initializer->reduced_value);
+#ifdef TOOLS_ENABLED
- ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
+ p_script->member_lines[name] = constant->start_line;
+#endif
+ } break;
- GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ const GDScriptParser::EnumNode::Value &enum_value = member.enum_value;
+ StringName name = enum_value.identifier->name;
- p_script->constants.insert(name, constant->value);
+ p_script->constants.insert(name, enum_value.value);
#ifdef TOOLS_ENABLED
-
- p_script->member_lines[name] = E->get().expression->line;
+ p_script->member_lines[name] = enum_value.identifier->start_line;
#endif
- }
+ } break;
- for (int i = 0; i < p_class->_signals.size(); i++) {
- StringName name = p_class->_signals[i].name;
+ case GDScriptParser::ClassNode::Member::SIGNAL: {
+ const GDScriptParser::SignalNode *signal = member.signal;
+ StringName name = signal->identifier->name;
- GDScript *c = p_script;
+ GDScript *c = p_script;
- while (c) {
- if (c->_signals.has(name)) {
- _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class);
- return ERR_ALREADY_EXISTS;
- }
+ while (c) {
+ if (c->_signals.has(name)) {
+ _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class);
+ return ERR_ALREADY_EXISTS;
+ }
- if (c->base.is_valid()) {
- c = c->base.ptr();
- } else {
- c = nullptr;
- }
- }
+ if (c->base.is_valid()) {
+ c = c->base.ptr();
+ } else {
+ c = nullptr;
+ }
+ }
- if (native.is_valid()) {
- if (ClassDB::has_signal(native->get_name(), name)) {
- _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class);
- return ERR_ALREADY_EXISTS;
- }
- }
+ if (native.is_valid()) {
+ if (ClassDB::has_signal(native->get_name(), name)) {
+ _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class);
+ return ERR_ALREADY_EXISTS;
+ }
+ }
+
+ Vector<StringName> parameters_names;
+ parameters_names.resize(signal->parameters.size());
+ for (int j = 0; j < signal->parameters.size(); j++) {
+ parameters_names.write[j] = signal->parameters[j]->identifier->name;
+ }
+ p_script->_signals[name] = parameters_names;
+ } break;
- p_script->_signals[name] = p_class->_signals[i].arguments;
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ const GDScriptParser::EnumNode *enum_n = member.m_enum;
+
+ // TODO: Make enums not be just a dictionary?
+ Dictionary new_enum;
+ for (int j = 0; j < enum_n->values.size(); j++) {
+ int value = enum_n->values[j].value;
+ // Needs to be string because Variant::get will convert to String.
+ new_enum[String(enum_n->values[j].identifier->name)] = value;
+ }
+
+ p_script->constants.insert(enum_n->identifier->name, new_enum);
+#ifdef TOOLS_ENABLED
+ p_script->member_lines[enum_n->identifier->name] = enum_n->start_line;
+#endif
+ } break;
+ default:
+ break; // Nothing to do here.
+ }
}
parsed_classes.insert(p_script);
@@ -2011,14 +2113,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
//parse sub-classes
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- StringName name = p_class->subclasses[i]->name;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = p_class->members[i];
+ if (member.type != member.CLASS) {
+ continue;
+ }
+ const GDScriptParser::ClassNode *inner_class = member.m_class;
+ StringName name = inner_class->identifier->name;
Ref<GDScript> &subclass = p_script->subclasses[name];
GDScript *subclass_ptr = subclass.ptr();
// Subclass might still be parsing, just skip it
if (!parsed_classes.has(subclass_ptr) && !parsing_classes.has(subclass_ptr)) {
- Error err = _parse_class_level(subclass_ptr, p_class->subclasses[i], p_keep_state);
+ Error err = _parse_class_level(subclass_ptr, inner_class, p_keep_state);
if (err) {
return err;
}
@@ -2026,7 +2133,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
#ifdef TOOLS_ENABLED
- p_script->member_lines[name] = p_class->subclasses[i]->line;
+ p_script->member_lines[name] = inner_class->start_line;
#endif
p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
@@ -2038,41 +2145,48 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
- bool has_initializer = false;
bool has_ready = false;
- for (int i = 0; i < p_class->functions.size(); i++) {
- if (!has_initializer && p_class->functions[i]->name == "_init") {
- has_initializer = true;
- }
- if (!has_ready && p_class->functions[i]->name == "_ready") {
- has_ready = true;
- }
- Error err = _parse_function(p_script, p_class, p_class->functions[i]);
- if (err) {
- return err;
- }
- }
-
- //parse static methods
-
- for (int i = 0; i < p_class->static_functions.size(); i++) {
- Error err = _parse_function(p_script, p_class, p_class->static_functions[i]);
- if (err) {
- return err;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = p_class->members[i];
+ if (member.type == member.FUNCTION) {
+ const GDScriptParser::FunctionNode *function = member.function;
+ if (!has_ready && function->identifier->name == "_ready") {
+ has_ready = true;
+ }
+ Error err = _parse_function(p_script, p_class, function);
+ if (err) {
+ return err;
+ }
+ } else if (member.type == member.VARIABLE) {
+ const GDScriptParser::VariableNode *variable = member.variable;
+ if (variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
+ if (variable->setter != nullptr) {
+ Error err = _parse_setter_getter(p_script, p_class, variable, true);
+ if (err) {
+ return err;
+ }
+ }
+ if (variable->getter != nullptr) {
+ Error err = _parse_setter_getter(p_script, p_class, variable, false);
+ if (err) {
+ return err;
+ }
+ }
+ }
}
}
- if (!has_initializer) {
- //create a constructor
+ {
+ // Create an implicit constructor in any case.
Error err = _parse_function(p_script, p_class, nullptr);
if (err) {
return err;
}
}
- if (!has_ready && p_class->ready->statements.size()) {
- //create a constructor
+ if (!has_ready && p_class->onready_used) {
+ //create a _ready constructor
Error err = _parse_function(p_script, p_class, nullptr, true);
if (err) {
return err;
@@ -2128,11 +2242,15 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
}
#endif
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- StringName name = p_class->subclasses[i]->name;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
+ }
+ const GDScriptParser::ClassNode *inner_class = p_class->members[i].m_class;
+ StringName name = inner_class->identifier->name;
GDScript *subclass = p_script->subclasses[name].ptr();
- Error err = _parse_class_blocks(subclass, p_class->subclasses[i], p_keep_state);
+ Error err = _parse_class_blocks(subclass, inner_class, p_keep_state);
if (err) {
return err;
}
@@ -2151,8 +2269,12 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
p_script->subclasses.clear();
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- StringName name = p_class->subclasses[i]->name;
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
+ }
+ const GDScriptParser::ClassNode *inner_class = p_class->members[i].m_class;
+ StringName name = inner_class->identifier->name;
Ref<GDScript> subclass;
String fully_qualified_name = p_script->fully_qualified_name + "::" + name;
@@ -2172,7 +2294,7 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
subclass->fully_qualified_name = fully_qualified_name;
p_script->subclasses.insert(name, subclass);
- _make_scripts(subclass.ptr(), p_class->subclasses[i], false);
+ _make_scripts(subclass.ptr(), inner_class, false);
}
}
@@ -2182,8 +2304,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
error = "";
parser = p_parser;
main_script = p_script;
- const GDScriptParser::Node *root = parser->get_parse_tree();
- ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
+ const GDScriptParser::ClassNode *root = parser->get_tree();
source = p_script->get_path();
@@ -2191,22 +2312,22 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
p_script->fully_qualified_name = p_script->path;
// Create scripts for subclasses beforehand so they can be referenced
- _make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ _make_scripts(p_script, root, p_keep_state);
p_script->_owner = nullptr;
- Error err = _parse_class_level(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ Error err = _parse_class_level(p_script, root, p_keep_state);
if (err) {
return err;
}
- err = _parse_class_blocks(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ err = _parse_class_blocks(p_script, root, p_keep_state);
if (err) {
return err;
}
- return OK;
+ return GDScriptCache::finish_compiling(p_script->get_path());
}
String GDScriptCompiler::get_error() const {
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 315d4f1842..db02079d26 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -33,106 +33,88 @@
#include "core/set.h"
#include "gdscript.h"
+#include "gdscript_codegen.h"
+#include "gdscript_function.h"
#include "gdscript_parser.h"
class GDScriptCompiler {
- const GDScriptParser *parser;
+ const GDScriptParser *parser = nullptr;
Set<GDScript *> parsed_classes;
Set<GDScript *> parsing_classes;
- GDScript *main_script;
+ GDScript *main_script = nullptr;
+
struct CodeGen {
- GDScript *script;
- const GDScriptParser::ClassNode *class_node;
- const GDScriptParser::FunctionNode *function_node;
- bool debug_stack;
-
- List<Map<StringName, int>> stack_id_stack;
- Map<StringName, int> stack_identifiers;
-
- List<GDScriptFunction::StackDebug> stack_debug;
- List<Map<StringName, int>> block_identifier_stack;
- Map<StringName, int> block_identifiers;
-
- void add_stack_identifier(const StringName &p_id, int p_stackpos) {
- stack_identifiers[p_id] = p_stackpos;
- if (debug_stack) {
- block_identifiers[p_id] = p_stackpos;
- GDScriptFunction::StackDebug sd;
- sd.added = true;
- sd.line = current_line;
- sd.identifier = p_id;
- sd.pos = p_stackpos;
- stack_debug.push_back(sd);
- }
+ GDScript *script = nullptr;
+ const GDScriptParser::ClassNode *class_node = nullptr;
+ const GDScriptParser::FunctionNode *function_node = nullptr;
+ StringName function_name;
+ GDScriptCodeGenerator *generator = nullptr;
+ Map<StringName, GDScriptCodeGenerator::Address> parameters;
+ Map<StringName, GDScriptCodeGenerator::Address> locals;
+ List<Set<StringName>> locals_in_scope;
+
+ GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) {
+ uint32_t addr = generator->add_local(p_name, p_type);
+ locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_VARIABLE, addr, p_type);
+ locals_in_scope.back()->get().insert(p_name);
+ return locals[p_name];
}
- void push_stack_identifiers() {
- stack_id_stack.push_back(stack_identifiers);
- if (debug_stack) {
- block_identifier_stack.push_back(block_identifiers);
- block_identifiers.clear();
- }
+ GDScriptCodeGenerator::Address add_local_constant(const StringName &p_name, const Variant &p_value) {
+ uint32_t addr = generator->add_local_constant(p_name, p_value);
+ locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_CONSTANT, addr);
+ return locals[p_name];
}
- void pop_stack_identifiers() {
- stack_identifiers = stack_id_stack.back()->get();
- stack_id_stack.pop_back();
-
- if (debug_stack) {
- for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) {
- GDScriptFunction::StackDebug sd;
- sd.added = false;
- sd.identifier = E->key();
- sd.line = current_line;
- sd.pos = E->get();
- stack_debug.push_back(sd);
- }
- block_identifiers = block_identifier_stack.back()->get();
- block_identifier_stack.pop_back();
- }
+ GDScriptCodeGenerator::Address add_temporary(const GDScriptDataType &p_type = GDScriptDataType()) {
+ uint32_t addr = generator->add_temporary();
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::TEMPORARY, addr, p_type);
}
- HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
- Map<StringName, int> name_map;
-#ifdef TOOLS_ENABLED
- Vector<StringName> named_globals;
-#endif
-
- int get_name_map_pos(const StringName &p_identifier) {
- int ret;
- if (!name_map.has(p_identifier)) {
- ret = name_map.size();
- name_map[p_identifier] = ret;
- } else {
- ret = name_map[p_identifier];
+ GDScriptCodeGenerator::Address add_constant(const Variant &p_constant) {
+ GDScriptDataType type;
+ type.has_type = true;
+ type.kind = GDScriptDataType::BUILTIN;
+ type.builtin_type = p_constant.get_type();
+ if (type.builtin_type == Variant::OBJECT) {
+ Object *obj = p_constant;
+ if (obj) {
+ type.kind = GDScriptDataType::NATIVE;
+ type.native_type = obj->get_class_name();
+
+ Ref<Script> script = obj->get_script();
+ if (script.is_valid()) {
+ type.script_type = script.ptr();
+ Ref<GDScript> gdscript = script;
+ if (gdscript.is_valid()) {
+ type.kind = GDScriptDataType::GDSCRIPT;
+ } else {
+ type.kind = GDScriptDataType::SCRIPT;
+ }
+ }
+ } else {
+ type.builtin_type = Variant::NIL;
+ }
}
- return ret;
- }
- int get_constant_pos(const Variant &p_constant) {
- if (constant_map.has(p_constant)) {
- return constant_map[p_constant];
- }
- int pos = constant_map.size();
- constant_map[p_constant] = pos;
- return pos;
+ uint32_t addr = generator->add_or_get_constant(p_constant);
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CONSTANT, addr, type);
}
- Vector<int> opcodes;
- void alloc_stack(int p_level) {
- if (p_level >= stack_max) {
- stack_max = p_level + 1;
- }
+ void start_block() {
+ Set<StringName> scope;
+ locals_in_scope.push_back(scope);
+ generator->start_block();
}
- void alloc_call(int p_params) {
- if (p_params >= call_max) {
- call_max = p_params;
+
+ void end_block() {
+ Set<StringName> &scope = locals_in_scope.back()->get();
+ for (Set<StringName>::Element *E = scope.front(); E; E = E->next()) {
+ locals.erase(E->get());
}
+ locals_in_scope.pop_back();
+ generator->end_block();
}
-
- int current_line;
- int stack_max;
- int call_max;
};
bool _is_class_member_property(CodeGen &codegen, const StringName &p_name);
@@ -140,15 +122,18 @@ class GDScriptCompiler {
void _set_error(const String &p_error, const GDScriptParser::Node *p_node);
- bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level);
- bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false, int p_index_addr = 0);
+ Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
+ Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
- GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const;
+ GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner = nullptr) const;
- int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level, int p_index_addr = 0);
- int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false, int p_index_addr = 0);
- Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
+ GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
+ GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
+ GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
+ void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
+ Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
+ Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
@@ -156,6 +141,7 @@ class GDScriptCompiler {
int err_column;
StringName source;
String error;
+ bool within_await = false;
public:
Error compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state = false);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 7433c4a5bc..2e372575da 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -33,9 +33,13 @@
#include "core/engine.h"
#include "core/global_constants.h"
#include "core/os/file_access.h"
+#include "gdscript_analyzer.h"
#include "gdscript_compiler.h"
+#include "gdscript_parser.h"
+#include "gdscript_tokenizer.h"
#ifdef TOOLS_ENABLED
+#include "core/project_settings.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#endif
@@ -114,16 +118,35 @@ void GDScriptLanguage::make_template(const String &p_class_name, const String &p
p_script->set_source_code(_template);
}
+static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, Map<int, String> &r_funcs) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) {
+ const GDScriptParser::FunctionNode *function = p_class->members[i].function;
+ r_funcs[function->start_line] = p_prefix.empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name);
+ } else if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {
+ String new_prefix = p_class->members[i].m_class->identifier->name;
+ get_function_names_recursively(p_class->members[i].m_class, p_prefix.empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs);
+ }
+ }
+}
+
bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
GDScriptParser parser;
+ GDScriptAnalyzer analyzer(&parser);
- Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path, false, r_safe_lines);
+ Error err = parser.parse(p_script, p_path, false);
+ if (err == OK) {
+ err = analyzer.analyze();
+ }
#ifdef DEBUG_ENABLED
if (r_warnings) {
for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
const GDScriptWarning &warn = E->get();
ScriptLanguage::Warning w;
- w.line = warn.line;
+ w.start_line = warn.start_line;
+ w.end_line = warn.end_line;
+ w.leftmost_column = warn.leftmost_column;
+ w.rightmost_column = warn.rightmost_column;
w.code = (int)warn.code;
w.string_code = GDScriptWarning::get_name_from_code(warn.code);
w.message = warn.get_message();
@@ -132,38 +155,33 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &
}
#endif
if (err) {
- r_line_error = parser.get_error_line();
- r_col_error = parser.get_error_column();
- r_test_error = parser.get_error();
+ GDScriptParser::ParserError parse_error = parser.get_errors().front()->get();
+ r_line_error = parse_error.line;
+ r_col_error = parse_error.column;
+ r_test_error = parse_error.message;
return false;
} else {
- const GDScriptParser::Node *root = parser.get_parse_tree();
- ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, false);
-
- const GDScriptParser::ClassNode *cl = static_cast<const GDScriptParser::ClassNode *>(root);
+ const GDScriptParser::ClassNode *cl = parser.get_tree();
Map<int, String> funcs;
- for (int i = 0; i < cl->functions.size(); i++) {
- funcs[cl->functions[i]->line] = cl->functions[i]->name;
- }
-
- for (int i = 0; i < cl->static_functions.size(); i++) {
- funcs[cl->static_functions[i]->line] = cl->static_functions[i]->name;
- }
- for (int i = 0; i < cl->subclasses.size(); i++) {
- for (int j = 0; j < cl->subclasses[i]->functions.size(); j++) {
- funcs[cl->subclasses[i]->functions[j]->line] = String(cl->subclasses[i]->name) + "." + cl->subclasses[i]->functions[j]->name;
- }
- for (int j = 0; j < cl->subclasses[i]->static_functions.size(); j++) {
- funcs[cl->subclasses[i]->static_functions[j]->line] = String(cl->subclasses[i]->name) + "." + cl->subclasses[i]->static_functions[j]->name;
- }
- }
+ get_function_names_recursively(cl, "", funcs);
for (Map<int, String>::Element *E = funcs.front(); E; E = E->next()) {
r_functions->push_back(E->get() + ":" + itos(E->key()));
}
}
+#ifdef DEBUG_ENABLED
+ if (r_safe_lines) {
+ const Set<int> &unsafe_lines = parser.get_unsafe_lines();
+ for (int i = 1; i <= parser.get_last_line_number(); i++) {
+ if (!unsafe_lines.has(i)) {
+ r_safe_lines->insert(i);
+ }
+ }
+ }
+#endif
+
return true;
}
@@ -176,20 +194,26 @@ bool GDScriptLanguage::supports_builtin_mode() const {
}
int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const {
- GDScriptTokenizerText tokenizer;
- tokenizer.set_code(p_code);
+ GDScriptTokenizer tokenizer;
+ tokenizer.set_source_code(p_code);
int indent = 0;
- while (tokenizer.get_token() != GDScriptTokenizer::TK_EOF && tokenizer.get_token() != GDScriptTokenizer::TK_ERROR) {
- if (tokenizer.get_token() == GDScriptTokenizer::TK_NEWLINE) {
- indent = tokenizer.get_token_line_indent();
- }
- if (indent == 0 && tokenizer.get_token() == GDScriptTokenizer::TK_PR_FUNCTION && tokenizer.get_token(1) == GDScriptTokenizer::TK_IDENTIFIER) {
- String identifier = tokenizer.get_token_identifier(1);
- if (identifier == p_function) {
- return tokenizer.get_token_line();
+ GDScriptTokenizer::Token current = tokenizer.scan();
+ while (current.type != GDScriptTokenizer::Token::TK_EOF && current.type != GDScriptTokenizer::Token::ERROR) {
+ if (current.type == GDScriptTokenizer::Token::INDENT) {
+ indent++;
+ } else if (current.type == GDScriptTokenizer::Token::DEDENT) {
+ indent--;
+ }
+ if (indent == 0 && current.type == GDScriptTokenizer::Token::FUNC) {
+ current = tokenizer.scan();
+ if (current.is_identifier()) {
+ String identifier = current.get_identifier();
+ if (identifier == p_function) {
+ return current.start_line;
+ }
}
}
- tokenizer.advance();
+ current = tokenizer.scan();
}
return -1;
}
@@ -397,16 +421,6 @@ void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const
}
{
MethodInfo mi;
- mi.name = "yield";
- mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
- mi.arguments.push_back(PropertyInfo(Variant::STRING, "signal"));
- mi.default_arguments.push_back(Variant());
- mi.default_arguments.push_back(String());
- mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "GDScriptFunctionState");
- p_functions->push_back(mi);
- }
- {
- MethodInfo mi;
mi.name = "assert";
mi.return_val.type = Variant::NIL;
mi.arguments.push_back(PropertyInfo(Variant::BOOL, "condition"));
@@ -467,44 +481,48 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
//////// COMPLETION //////////
-#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-
-struct GDScriptCompletionContext {
- const GDScriptParser::ClassNode *_class = nullptr;
- const GDScriptParser::FunctionNode *function = nullptr;
- const GDScriptParser::BlockNode *block = nullptr;
- Object *base = nullptr;
- String base_path;
- int line = 0;
- uint32_t depth = 0;
+#ifdef TOOLS_ENABLED
- GDScriptCompletionContext() {}
-};
+#define COMPLETION_RECURSION_LIMIT 200
struct GDScriptCompletionIdentifier {
GDScriptParser::DataType type;
String enumeration;
Variant value;
- const GDScriptParser::Node *assigned_expression = nullptr;
-
- GDScriptCompletionIdentifier() {}
+ const GDScriptParser::ExpressionNode *assigned_expression = nullptr;
};
-static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptCodeCompletionOption> &r_list) {
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
-
- for (int i = 0; i < p_dir->get_file_count(); i++) {
- ScriptCodeCompletionOption option(p_dir->get_file_path(i), ScriptCodeCompletionOption::KIND_FILE_PATH);
- option.insert_text = quote_style + option.display + quote_style;
- r_list.insert(option.display, option);
- }
-
- for (int i = 0; i < p_dir->get_subdir_count(); i++) {
- _get_directory_contents(p_dir->get_subdir(i), r_list);
+// TODO: Move this to a central location (maybe core?).
+static const char *underscore_classes[] = {
+ "ClassDB",
+ "Directory",
+ "Engine",
+ "File",
+ "Geometry",
+ "GodotSharp",
+ "JSON",
+ "Marshalls",
+ "Mutex",
+ "OS",
+ "ResourceLoader",
+ "ResourceSaver",
+ "Semaphore",
+ "Thread",
+ "VisualScriptEditor",
+ nullptr,
+};
+static StringName _get_real_class_name(const StringName &p_source) {
+ const char **class_name = underscore_classes;
+ while (*class_name != nullptr) {
+ if (p_source == *class_name) {
+ return String("_") + p_source;
+ }
+ class_name++;
}
+ return p_source;
}
-static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = true) {
+static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
String enum_name = p_info.class_name;
if (enum_name.find(".") == -1) {
@@ -527,7 +545,7 @@ static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = tr
}
}
if (p_info.type == Variant::NIL) {
- if (p_isarg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ if (p_is_arg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
return "Variant";
} else {
return "void";
@@ -537,11 +555,541 @@ static String _get_visual_datatype(const PropertyInfo &p_info, bool p_isarg = tr
return Variant::get_type_name(p_info.type);
}
+static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool p_is_annotation = false) {
+ String arghint;
+ if (!p_is_annotation) {
+ arghint += _get_visual_datatype(p_info.return_val, false) + " ";
+ }
+ arghint += p_info.name + "(";
+
+ int def_args = p_info.arguments.size() - p_info.default_arguments.size();
+ int i = 0;
+ for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E; E = E->next()) {
+ if (i > 0) {
+ arghint += ", ";
+ }
+
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += E->get().name + ": " + _get_visual_datatype(E->get(), true);
+
+ if (i - def_args >= 0) {
+ arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();
+ }
+
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+
+ i++;
+ }
+
+ if (p_info.flags & METHOD_FLAG_VARARG) {
+ if (p_info.arguments.size() > 0) {
+ arghint += ", ";
+ }
+ if (p_arg_idx >= p_info.arguments.size()) {
+ arghint += String::chr(0xFFFF);
+ }
+ arghint += "...";
+ if (p_arg_idx >= p_info.arguments.size()) {
+ arghint += String::chr(0xFFFF);
+ }
+ }
+
+ arghint += ")";
+
+ return arghint;
+}
+
+static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx) {
+ String arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name.operator String() + "(";
+
+ for (int i = 0; i < p_function->parameters.size(); i++) {
+ if (i > 0) {
+ arghint += ", ";
+ }
+
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ const GDScriptParser::ParameterNode *par = p_function->parameters[i];
+ arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string();
+
+ if (par->default_value) {
+ String def_val = "<unknown>";
+ if (par->default_value->type == GDScriptParser::Node::LITERAL) {
+ const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->default_value);
+ def_val = literal->value.get_construct_string();
+ } else if (par->default_value->type == GDScriptParser::Node::IDENTIFIER) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->default_value);
+ def_val = id->name.operator String();
+ }
+ arghint += " = " + def_val;
+ }
+ if (i == p_arg_idx) {
+ arghint += String::chr(0xFFFF);
+ }
+ }
+
+ arghint += ")";
+
+ return arghint;
+}
+
+static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptCodeCompletionOption> &r_list) {
+ const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
+
+ for (int i = 0; i < p_dir->get_file_count(); i++) {
+ ScriptCodeCompletionOption option(p_dir->get_file_path(i), ScriptCodeCompletionOption::KIND_FILE_PATH);
+ option.insert_text = quote_style + option.display + quote_style;
+ r_list.insert(option.display, option);
+ }
+
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ _get_directory_contents(p_dir->get_subdir(i), r_list);
+ }
+}
+
+static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptCodeCompletionOption> &r_result) {
+ if (p_annotation->name == "@export_range" || p_annotation->name == "@export_exp_range") {
+ if (p_argument == 3 || p_argument == 4) {
+ // Slider hint.
+ ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ slider1.insert_text = p_quote_style + slider1.display + p_quote_style;
+ r_result.insert(slider1.display, slider1);
+ ScriptCodeCompletionOption slider2("or_lesser", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ slider2.insert_text = p_quote_style + slider2.display + p_quote_style;
+ r_result.insert(slider2.display, slider2);
+ }
+ } else if (p_annotation->name == "@export_exp_easing") {
+ if (p_argument == 0 || p_argument == 1) {
+ // Easing hint.
+ ScriptCodeCompletionOption hint1("attenuation", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ hint1.insert_text = p_quote_style + hint1.display + p_quote_style;
+ r_result.insert(hint1.display, hint1);
+ ScriptCodeCompletionOption hint2("inout", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ hint2.insert_text = p_quote_style + hint2.display + p_quote_style;
+ r_result.insert(hint2.display, hint2);
+ }
+ } else if (p_annotation->name == "@export_node_path") {
+ ScriptCodeCompletionOption node("Node", ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(node.display, node);
+ List<StringName> node_types;
+ ClassDB::get_inheriters_from_class("Node", &node_types);
+ for (const List<StringName>::Element *E = node_types.front(); E != nullptr; E = E->next()) {
+ if (!ClassDB::is_class_exposed(E->get())) {
+ continue;
+ }
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(option.display, option);
+ }
+ }
+}
+
+static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, Map<String, ScriptCodeCompletionOption> &r_result) {
+ List<StringName> native_types;
+ ClassDB::get_class_list(&native_types);
+ for (const List<StringName>::Element *E = native_types.front(); E != nullptr; E = E->next()) {
+ if (ClassDB::is_class_exposed(E->get()) && !Engine::get_singleton()->has_singleton(E->get())) {
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(option.display, option);
+ }
+ }
+
+ if (p_context.current_class) {
+ if (!p_inherit_only && p_context.current_class->base_type.is_set()) {
+ // Native enums from base class
+ List<StringName> enums;
+ ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums);
+ for (const List<StringName>::Element *E = enums.front(); E != nullptr; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_ENUM);
+ r_result.insert(option.display, option);
+ }
+ }
+ // Check current class for potential types
+ const GDScriptParser::ClassNode *current = p_context.current_class;
+ while (current) {
+ for (int i = 0; i < current->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = current->members[i];
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::CLASS: {
+ ScriptCodeCompletionOption option(member.m_class->identifier->name, ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(option.display, option);
+ } break;
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ if (!p_inherit_only) {
+ ScriptCodeCompletionOption option(member.m_enum->identifier->name, ScriptCodeCompletionOption::KIND_ENUM);
+ r_result.insert(option.display, option);
+ }
+ } break;
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ if (member.constant->get_datatype().is_meta_type && p_context.current_class->outer != nullptr) {
+ ScriptCodeCompletionOption option(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(option.display, option);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ current = current->outer;
+ }
+ }
+
+ // Global scripts
+ List<StringName> global_classes;
+ ScriptServer::get_global_class_list(&global_classes);
+ for (const List<StringName>::Element *E = global_classes.front(); E != nullptr; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(option.display, option);
+ }
+
+ // Autoload singletons
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->get();
+ if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") {
+ continue;
+ }
+ ScriptCodeCompletionOption option(info.name, ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(option.display, option);
+ }
+}
+
+static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, Map<String, ScriptCodeCompletionOption> &r_result) {
+ for (int i = 0; i < p_suite->locals.size(); i++) {
+ ScriptCodeCompletionOption option(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_VARIABLE);
+ r_result.insert(option.display, option);
+ }
+ if (p_suite->parent_block) {
+ _find_identifiers_in_suite(p_suite->parent_block, r_result);
+ }
+}
+
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth);
+
+static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) {
+ ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
+
+ if (!p_parent_only) {
+ bool outer = false;
+ const GDScriptParser::ClassNode *clss = p_class;
+ while (clss) {
+ for (int i = 0; i < clss->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = clss->members[i];
+ ScriptCodeCompletionOption option;
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::VARIABLE:
+ if (p_only_functions || outer || (p_static)) {
+ continue;
+ }
+ option = ScriptCodeCompletionOption(member.variable->identifier->name, ScriptCodeCompletionOption::KIND_MEMBER);
+ break;
+ case GDScriptParser::ClassNode::Member::CONSTANT:
+ if (p_only_functions) {
+ continue;
+ }
+ option = ScriptCodeCompletionOption(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT);
+ break;
+ case GDScriptParser::ClassNode::Member::CLASS:
+ if (p_only_functions) {
+ continue;
+ }
+ option = ScriptCodeCompletionOption(member.m_class->identifier->name, ScriptCodeCompletionOption::KIND_CLASS);
+ break;
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE:
+ if (p_only_functions) {
+ continue;
+ }
+ option = ScriptCodeCompletionOption(member.enum_value.identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT);
+ break;
+ case GDScriptParser::ClassNode::Member::ENUM:
+ if (p_only_functions) {
+ continue;
+ }
+ option = ScriptCodeCompletionOption(member.m_enum->identifier->name, ScriptCodeCompletionOption::KIND_ENUM);
+ break;
+ case GDScriptParser::ClassNode::Member::FUNCTION:
+ if (outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) {
+ continue;
+ }
+ option = ScriptCodeCompletionOption(member.function->identifier->name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (member.function->parameters.size() > 0) {
+ option.insert_text += "(";
+ } else {
+ option.insert_text += "()";
+ }
+ break;
+ case GDScriptParser::ClassNode::Member::SIGNAL:
+ if (p_only_functions || outer) {
+ continue;
+ }
+ option = ScriptCodeCompletionOption(member.signal->identifier->name, ScriptCodeCompletionOption::KIND_SIGNAL);
+ break;
+ case GDScriptParser::ClassNode::Member::UNDEFINED:
+ break;
+ }
+ r_result.insert(option.display, option);
+ }
+ outer = true;
+ clss = clss->outer;
+ }
+ }
+
+ // Parents.
+ GDScriptCompletionIdentifier base_type;
+ base_type.type = p_class->base_type;
+ base_type.type.is_meta_type = p_static;
+
+ _find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1);
+}
+
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) {
+ ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
+
+ GDScriptParser::DataType base_type = p_base.type;
+ bool _static = base_type.is_meta_type;
+
+ if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) {
+ ScriptCodeCompletionOption option("new", ScriptCodeCompletionOption::KIND_FUNCTION);
+ option.insert_text += "(";
+ r_result.insert(option.display, option);
+ }
+
+ while (!base_type.has_no_type()) {
+ switch (base_type.kind) {
+ case GDScriptParser::DataType::CLASS: {
+ _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result, p_recursion_depth + 1);
+ // This already finds all parent identifiers, so we are done.
+ base_type = GDScriptParser::DataType();
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> scr = base_type.script_type;
+ if (scr.is_valid()) {
+ if (!p_only_functions) {
+ if (!_static) {
+ List<PropertyInfo> members;
+ scr->get_script_property_list(&members);
+ for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ r_result.insert(option.display, option);
+ }
+ }
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
+ for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ r_result.insert(option.display, option);
+ }
+
+ List<MethodInfo> signals;
+ scr->get_script_signal_list(&signals);
+ for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL);
+ r_result.insert(option.display, option);
+ }
+ }
+
+ List<MethodInfo> methods;
+ scr->get_script_method_list(&methods);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name.begins_with("@")) {
+ continue;
+ }
+ ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E->get().arguments.size()) {
+ option.insert_text += "(";
+ } else {
+ option.insert_text += "()";
+ }
+ r_result.insert(option.display, option);
+ }
+
+ Ref<Script> base_script = scr->get_base_script();
+ if (base_script.is_valid()) {
+ base_type.script_type = base_script;
+ } else {
+ base_type.kind = GDScriptParser::DataType::NATIVE;
+ base_type.native_type = scr->get_instance_base_type();
+ }
+ } else {
+ return;
+ }
+ } break;
+ case GDScriptParser::DataType::NATIVE: {
+ StringName type = _get_real_class_name(base_type.native_type);
+ if (!ClassDB::class_exists(type)) {
+ return;
+ }
+
+ if (!p_only_functions) {
+ List<String> constants;
+ ClassDB::get_integer_constant_list(type, &constants);
+ for (List<String>::Element *E = constants.front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ r_result.insert(option.display, option);
+ }
+
+ if (!_static || Engine::get_singleton()->has_singleton(type)) {
+ List<PropertyInfo> pinfo;
+ ClassDB::get_property_list(type, &pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
+ continue;
+ }
+ if (E->get().name.find("/") != -1) {
+ continue;
+ }
+ ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ r_result.insert(option.display, option);
+ }
+ }
+ }
+
+ if (!_static || Engine::get_singleton()->has_singleton(type)) {
+ List<MethodInfo> methods;
+ bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize();
+ ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters);
+ for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ if (E->get().name.begins_with("_")) {
+ continue;
+ }
+ ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E->get().arguments.size()) {
+ option.insert_text += "(";
+ } else {
+ option.insert_text += "()";
+ }
+ r_result.insert(option.display, option);
+ }
+ }
+
+ return;
+ } break;
+ case GDScriptParser::DataType::BUILTIN: {
+ Callable::CallError err;
+ Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ if (err.error != Callable::CallError::CALL_OK) {
+ return;
+ }
+
+ if (!p_only_functions) {
+ List<PropertyInfo> members;
+ if (p_base.value.get_type() != Variant::NIL) {
+ p_base.value.get_property_list(&members);
+ } else {
+ tmp.get_property_list(&members);
+ }
+
+ for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
+ if (String(E->get().name).find("/") == -1) {
+ ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ r_result.insert(option.display, option);
+ }
+ }
+ }
+
+ List<MethodInfo> methods;
+ tmp.get_method_list(&methods);
+ for (const List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E->get().arguments.size()) {
+ option.insert_text += "(";
+ } else {
+ option.insert_text += "()";
+ }
+ r_result.insert(option.display, option);
+ }
+
+ return;
+ } break;
+ default: {
+ return;
+ } break;
+ }
+ }
+}
+
+static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) {
+ if (!p_only_functions && p_context.current_suite) {
+ // This includes function parameters, since they are also locals.
+ _find_identifiers_in_suite(p_context.current_suite, r_result);
+ }
+
+ if (p_context.current_class) {
+ _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1);
+ }
+
+ for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
+ MethodInfo function = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
+ ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
+ option.insert_text += "(";
+ } else {
+ option.insert_text += "()";
+ }
+ r_result.insert(option.display, option);
+ }
+
+ if (p_only_functions) {
+ return;
+ }
+
+ static const char *_type_names[Variant::VARIANT_MAX] = {
+ "null", "bool", "int", "float", "String", "StringName", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform",
+ "Color", "NodePath", "RID", "Signal", "Callable", "Object", "Dictionary", "Array", "PackedByteArray", "PackedInt32Array", "PackedInt64Array", "PackedFloat32Array", "PackedFloat64Array", "PackedStringArray",
+ "PackedVector2Array", "PackedVector3Array", "PackedColorArray"
+ };
+ static_assert((sizeof(_type_names) / sizeof(*_type_names)) == Variant::VARIANT_MAX, "Completion for builtin types is incomplete");
+
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ ScriptCodeCompletionOption option(_type_names[i], ScriptCodeCompletionOption::KIND_CLASS);
+ r_result.insert(option.display, option);
+ }
+
+ static const char *_keywords[] = {
+ "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
+ "breakpoint", "class", "extends", "is", "func", "preload", "signal", "tool", "await",
+ "const", "enum", "static", "super", "var", "break", "continue", "if", "elif",
+ "else", "for", "pass", "return", "match", "while",
+ 0
+ };
+
+ const char **kw = _keywords;
+ while (*kw) {
+ ScriptCodeCompletionOption option(*kw, ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ r_result.insert(option.display, option);
+ kw++;
+ }
+
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) {
+ if (!E->value().is_singleton) {
+ continue;
+ }
+ ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ r_result.insert(option.display, option);
+ }
+
+ // Native classes and global constants.
+ for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option;
+ if (ClassDB::class_exists(E->key()) || Engine::get_singleton()->has_singleton(E->key())) {
+ option = ScriptCodeCompletionOption(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
+ } else {
+ option = ScriptCodeCompletionOption(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ }
+ r_result.insert(option.display, option);
+ }
+}
+
static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
GDScriptCompletionIdentifier ci;
ci.value = p_value;
ci.type.is_constant = true;
- ci.type.has_type = true;
+ ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
ci.type.kind = GDScriptParser::DataType::BUILTIN;
ci.type.builtin_type = p_value.get_type();
@@ -560,12 +1108,7 @@ static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
}
if (scr.is_valid()) {
ci.type.script_type = scr;
- Ref<GDScript> gds = scr;
- if (gds.is_valid()) {
- ci.type.kind = GDScriptParser::DataType::GDSCRIPT;
- } else {
- ci.type.kind = GDScriptParser::DataType::SCRIPT;
- }
+ ci.type.kind = GDScriptParser::DataType::SCRIPT;
ci.type.native_type = scr->get_instance_base_type();
} else {
ci.type.kind = GDScriptParser::DataType::NATIVE;
@@ -587,7 +1130,7 @@ static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_pr
ci.enumeration = p_property.class_name;
}
- ci.type.has_type = true;
+ ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
ci.type.builtin_type = p_property.type;
if (p_property.type == Variant::OBJECT) {
ci.type.kind = GDScriptParser::DataType::NATIVE;
@@ -598,344 +1141,292 @@ static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_pr
return ci;
}
-static GDScriptCompletionIdentifier _type_from_gdtype(const GDScriptDataType &p_gdtype) {
- GDScriptCompletionIdentifier ci;
- if (!p_gdtype.has_type) {
- return ci;
- }
-
- ci.type.has_type = true;
- ci.type.builtin_type = p_gdtype.builtin_type;
- ci.type.native_type = p_gdtype.native_type;
- ci.type.script_type = p_gdtype.script_type;
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
+static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
- switch (p_gdtype.kind) {
- case GDScriptDataType::UNINITIALIZED: {
- ERR_PRINT("Uninitialized completion. Please report a bug.");
- } break;
- case GDScriptDataType::BUILTIN: {
- ci.type.kind = GDScriptParser::DataType::BUILTIN;
- } break;
- case GDScriptDataType::NATIVE: {
- ci.type.kind = GDScriptParser::DataType::NATIVE;
- } break;
- case GDScriptDataType::GDSCRIPT: {
- ci.type.kind = GDScriptParser::DataType::GDSCRIPT;
- } break;
- case GDScriptDataType::SCRIPT: {
- ci.type.kind = GDScriptParser::DataType::SCRIPT;
- } break;
- }
- return ci;
-}
-
-static bool _guess_identifier_type(GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
-static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
-static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
-
-static bool _guess_expression_type(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_expression, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) {
bool found = false;
- if (++p_context.depth > 100) {
- print_error("Maximum _guess_expression_type depth limit reached. Please file a bugreport.");
- return false;
- }
-
- switch (p_expression->type) {
- case GDScriptParser::Node::TYPE_CONSTANT: {
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(p_expression);
- r_type = _type_from_variant(cn->value);
- found = true;
- } break;
- case GDScriptParser::Node::TYPE_SELF: {
- if (p_context._class) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::CLASS;
- r_type.type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
- r_type.type.is_constant = true;
- r_type.value = p_context.base;
+ if (p_expression->is_constant) {
+ // Already has a value, so just use that.
+ r_type = _type_from_variant(p_expression->reduced_value);
+ found = true;
+ } else {
+ switch (p_expression->type) {
+ case GDScriptParser::Node::LITERAL: {
+ const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(p_expression);
+ r_type = _type_from_variant(literal->value);
found = true;
- }
- } break;
- case GDScriptParser::Node::TYPE_IDENTIFIER: {
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
- found = _guess_identifier_type(p_context, id->name, r_type);
- } break;
- case GDScriptParser::Node::TYPE_DICTIONARY: {
- // Try to recreate the dictionary
- const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
- Dictionary d;
- bool full = true;
- for (int i = 0; i < dn->elements.size(); i++) {
- GDScriptCompletionIdentifier key;
- if (_guess_expression_type(p_context, dn->elements[i].key, key)) {
- GDScriptCompletionIdentifier value;
- if (_guess_expression_type(p_context, dn->elements[i].value, value)) {
- if (!value.type.is_constant) {
+ } break;
+ case GDScriptParser::Node::SELF: {
+ if (p_context.current_class) {
+ r_type.type.kind = GDScriptParser::DataType::CLASS;
+ r_type.type.type_source = GDScriptParser::DataType::INFERRED;
+ r_type.type.is_constant = true;
+ r_type.type.class_type = p_context.current_class;
+ r_type.value = p_context.base;
+ found = true;
+ }
+ } break;
+ case GDScriptParser::Node::IDENTIFIER: {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
+ found = _guess_identifier_type(p_context, id->name, r_type);
+ } break;
+ case GDScriptParser::Node::DICTIONARY: {
+ // Try to recreate the dictionary.
+ const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
+ Dictionary d;
+ bool full = true;
+ for (int i = 0; i < dn->elements.size(); i++) {
+ GDScriptCompletionIdentifier key;
+ if (_guess_expression_type(p_context, dn->elements[i].key, key)) {
+ if (!key.type.is_constant) {
+ full = false;
+ break;
+ }
+ GDScriptCompletionIdentifier value;
+ if (_guess_expression_type(p_context, dn->elements[i].value, value)) {
+ if (!value.type.is_constant) {
+ full = false;
+ break;
+ }
+ d[key.value] = value.value;
+ } else {
full = false;
break;
}
- d[key.value] = value.value;
} else {
full = false;
break;
}
- } else {
- full = false;
- break;
}
- }
- if (full) {
- // If not fully constant, setting this value is detrimental to the inference
- r_type.value = d;
- r_type.type.is_constant = true;
- }
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::BUILTIN;
- r_type.type.builtin_type = Variant::DICTIONARY;
- } break;
- case GDScriptParser::Node::TYPE_ARRAY: {
- // Try to recreate the array
- const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
- Array a;
- bool full = true;
- a.resize(an->elements.size());
- for (int i = 0; i < an->elements.size(); i++) {
- GDScriptCompletionIdentifier value;
- if (_guess_expression_type(p_context, an->elements[i], value)) {
- a[i] = value.value;
- } else {
- full = false;
- break;
+ if (full) {
+ r_type.value = d;
+ r_type.type.is_constant = true;
}
- }
- if (full) {
- // If not fully constant, setting this value is detrimental to the inference
- r_type.value = a;
- }
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::BUILTIN;
- r_type.type.builtin_type = Variant::ARRAY;
- } break;
- case GDScriptParser::Node::TYPE_CAST: {
- const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
- GDScriptCompletionIdentifier value;
- if (_guess_expression_type(p_context, cn->source_node, r_type)) {
- r_type.type = cn->get_datatype();
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::DICTIONARY;
found = true;
- }
- } break;
- case GDScriptParser::Node::TYPE_OPERATOR: {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_expression);
- switch (op->op) {
- case GDScriptParser::OperatorNode::OP_CALL: {
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::BUILTIN;
- r_type.type.builtin_type = tn->vtype;
- found = true;
- break;
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
- const GDScriptParser::BuiltInFunctionNode *bin = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
- MethodInfo mi = GDScriptFunctions::get_info(bin->function);
- r_type = _type_from_property(mi.return_val);
- found = true;
+ } break;
+ case GDScriptParser::Node::ARRAY: {
+ // Try to recreate the array
+ const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
+ Array a;
+ bool full = true;
+ a.resize(an->elements.size());
+ for (int i = 0; i < an->elements.size(); i++) {
+ GDScriptCompletionIdentifier value;
+ if (_guess_expression_type(p_context, an->elements[i], value)) {
+ if (value.type.is_constant) {
+ a[i] = value.value;
+ } else {
+ full = false;
+ break;
+ }
+ } else {
+ full = false;
break;
- } else if (op->arguments.size() >= 2 && op->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1])->name;
+ }
+ }
+ if (full) {
+ // If not fully constant, setting this value is detrimental to the inference.
+ r_type.value = a;
+ }
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::ARRAY;
+ found = true;
+ } break;
+ case GDScriptParser::Node::CAST: {
+ const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
+ GDScriptCompletionIdentifier value;
+ if (_guess_expression_type(p_context, cn->operand, r_type)) {
+ r_type.type = cn->get_datatype();
+ found = true;
+ }
+ } break;
+ case GDScriptParser::Node::CALL: {
+ const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
+ if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name);
+ found = true;
+ break;
+ } else if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
+ MethodInfo mi = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+ r_type = _type_from_property(mi.return_val);
+ found = true;
+ break;
+ } else {
+ GDScriptParser::CompletionContext c = p_context;
+ c.current_line = call->start_line;
- GDScriptCompletionContext c = p_context;
- c.line = op->line;
+ GDScriptParser::Node::Type callee_type = call->get_callee_type();
- GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(c, op->arguments[0], base)) {
+ GDScriptCompletionIdentifier base;
+ if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) {
+ // Simple call, so base is 'self'.
+ if (p_context.current_class) {
+ base.type.kind = GDScriptParser::DataType::CLASS;
+ base.type.type_source = GDScriptParser::DataType::INFERRED;
+ base.type.is_constant = true;
+ base.type.class_type = p_context.current_class;
+ base.value = p_context.base;
+ } else {
+ break;
+ }
+ } else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->is_attribute) {
+ if (!_guess_expression_type(c, static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->base, base)) {
found = false;
break;
}
+ } else {
+ break;
+ }
- // Try call if constant methods with constant arguments
- if (base.type.is_constant && base.value.get_type() == Variant::OBJECT) {
- GDScriptParser::DataType native_type = base.type;
+ // Try call if constant methods with constant arguments
+ if (base.type.is_constant && base.value.get_type() == Variant::OBJECT) {
+ GDScriptParser::DataType native_type = base.type;
- while (native_type.kind == GDScriptParser::DataType::CLASS) {
- native_type = native_type.class_type->base_type;
- }
+ while (native_type.kind == GDScriptParser::DataType::CLASS) {
+ native_type = native_type.class_type->base_type;
+ }
- while (native_type.kind == GDScriptParser::DataType::GDSCRIPT || native_type.kind == GDScriptParser::DataType::SCRIPT) {
- if (native_type.script_type.is_valid()) {
- Ref<Script> parent = native_type.script_type->get_base_script();
- if (parent.is_valid()) {
- native_type.script_type = parent;
- } else {
- native_type.kind = GDScriptParser::DataType::NATIVE;
- native_type.native_type = native_type.script_type->get_instance_base_type();
+ while (native_type.kind == GDScriptParser::DataType::SCRIPT) {
+ if (native_type.script_type.is_valid()) {
+ Ref<Script> parent = native_type.script_type->get_base_script();
+ if (parent.is_valid()) {
+ native_type.script_type = parent;
+ } else {
+ native_type.kind = GDScriptParser::DataType::NATIVE;
+ native_type.native_type = native_type.script_type->get_instance_base_type();
+ if (!ClassDB::class_exists(native_type.native_type)) {
+ native_type.native_type = String("_") + native_type.native_type;
if (!ClassDB::class_exists(native_type.native_type)) {
- native_type.native_type = String("_") + native_type.native_type;
- if (!ClassDB::class_exists(native_type.native_type)) {
- native_type.has_type = false;
- }
+ native_type.kind = GDScriptParser::DataType::UNRESOLVED;
}
}
}
}
+ }
- if (native_type.has_type && native_type.kind == GDScriptParser::DataType::NATIVE) {
- MethodBind *mb = ClassDB::get_method(native_type.native_type, id);
- if (mb && mb->is_const()) {
- bool all_is_const = true;
- Vector<Variant> args;
- GDScriptCompletionContext c2 = p_context;
- c2.line = op->line;
- for (int i = 2; all_is_const && i < op->arguments.size(); i++) {
- GDScriptCompletionIdentifier arg;
-
- if (_guess_expression_type(c2, op->arguments[i], arg)) {
- if (arg.type.has_type && arg.type.is_constant && arg.value.get_type() != Variant::OBJECT) {
- args.push_back(arg.value);
- } else {
- all_is_const = false;
- }
- } else {
- all_is_const = false;
- }
+ if (native_type.kind == GDScriptParser::DataType::NATIVE) {
+ MethodBind *mb = ClassDB::get_method(native_type.native_type, call->function_name);
+ if (mb && mb->is_const()) {
+ bool all_is_const = true;
+ Vector<Variant> args;
+ GDScriptParser::CompletionContext c2 = p_context;
+ c2.current_line = call->start_line;
+ for (int i = 0; all_is_const && i < call->arguments.size(); i++) {
+ GDScriptCompletionIdentifier arg;
+
+ if (!call->arguments[i]->is_constant) {
+ all_is_const = false;
}
+ }
- Object *baseptr = base.value;
-
- if (all_is_const && String(id) == "get_node" && ClassDB::is_parent_class(native_type.native_type, "Node") && args.size()) {
- String arg1 = args[0];
- if (arg1.begins_with("/root/")) {
- String which = arg1.get_slice("/", 2);
- if (which != "") {
- // Try singletons first
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) {
- r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
- found = true;
- } else {
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
- }
- String name = s.get_slice("/", 1);
- if (name == which) {
- String script = ProjectSettings::get_singleton()->get(s);
+ Object *baseptr = base.value;
- if (script.begins_with("*")) {
- script = script.right(1);
- }
+ if (all_is_const && String(call->function_name) == "get_node" && ClassDB::is_parent_class(native_type.native_type, "Node") && args.size()) {
+ String arg1 = args[0];
+ if (arg1.begins_with("/root/")) {
+ String which = arg1.get_slice("/", 2);
+ if (which != "") {
+ // Try singletons first
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) {
+ r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
+ found = true;
+ } else {
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- if (!script.begins_with("res://")) {
- script = "res://" + script;
- }
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ String name = E->key();
+ if (name == which) {
+ String script = E->value().path;
- if (!script.ends_with(".gd")) {
- //not a script, try find the script anyway,
- //may have some success
- script = script.get_basename() + ".gd";
- }
+ if (!script.begins_with("res://")) {
+ script = "res://" + script;
+ }
+
+ if (!script.ends_with(".gd")) {
+ //not a script, try find the script anyway,
+ //may have some success
+ script = script.get_basename() + ".gd";
+ }
- if (FileAccess::exists(script)) {
- Ref<Script> scr;
- if (ScriptCodeCompletionCache::get_singleton()) {
- scr = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(script);
- } else {
- scr = ResourceLoader::load(script);
- }
- if (scr.is_valid()) {
- r_type.type.has_type = true;
- r_type.type.script_type = scr;
- r_type.type.is_constant = false;
- Ref<GDScript> gds = scr;
- if (gds.is_valid()) {
- r_type.type.kind = GDScriptParser::DataType::GDSCRIPT;
- } else {
- r_type.type.kind = GDScriptParser::DataType::SCRIPT;
- }
- r_type.value = Variant();
- found = true;
- }
+ if (FileAccess::exists(script)) {
+ Error err = OK;
+ Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
+ if (err == OK) {
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.script_path = script;
+ r_type.type.class_type = parser->get_parser()->get_tree();
+ r_type.type.is_constant = false;
+ r_type.type.kind = GDScriptParser::DataType::CLASS;
+ r_type.value = Variant();
+ p_context.dependent_parsers.push_back(parser);
+ found = true;
}
- break;
}
+ break;
}
}
}
}
}
+ }
- if (!found && all_is_const && baseptr) {
- Vector<const Variant *> argptr;
- for (int i = 0; i < args.size(); i++) {
- argptr.push_back(&args[i]);
- }
+ if (!found && all_is_const && baseptr) {
+ Vector<const Variant *> argptr;
+ for (int i = 0; i < args.size(); i++) {
+ argptr.push_back(&args[i]);
+ }
- Callable::CallError ce;
- Variant ret = mb->call(baseptr, (const Variant **)argptr.ptr(), argptr.size(), ce);
+ Callable::CallError ce;
+ Variant ret = mb->call(baseptr, (const Variant **)argptr.ptr(), argptr.size(), ce);
- if (ce.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
- if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != nullptr) {
- r_type = _type_from_variant(ret);
- found = true;
- }
+ if (ce.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
+ if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != nullptr) {
+ r_type = _type_from_variant(ret);
+ found = true;
}
}
}
}
}
-
- if (!found) {
- found = _guess_method_return_type_from_base(c, base, id, r_type);
- }
}
- } break;
- case GDScriptParser::OperatorNode::OP_PARENT_CALL: {
- if (!p_context._class || !op->arguments.size() || op->arguments[0]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
- break;
- }
-
- StringName id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
-
- GDScriptCompletionIdentifier base;
- base.value = p_context.base;
- base.type = p_context._class->base_type;
-
- GDScriptCompletionContext c = p_context;
- c.line = op->line;
- found = _guess_method_return_type_from_base(c, base, id, r_type);
- } break;
- case GDScriptParser::OperatorNode::OP_INDEX_NAMED: {
- if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
- found = false;
- break;
+ if (!found) {
+ found = _guess_method_return_type_from_base(c, base, call->function_name, r_type);
}
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
-
- GDScriptCompletionContext c = p_context;
- c.line = op->line;
+ }
+ } break;
+ case GDScriptParser::Node::SUBSCRIPT: {
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);
+ if (subscript->is_attribute) {
+ GDScriptParser::CompletionContext c = p_context;
+ c.current_line = subscript->start_line;
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(c, op->arguments[0], base)) {
+ if (!_guess_expression_type(c, subscript->base, base)) {
found = false;
break;
}
- if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(id->name))) {
- Variant value = base.value.operator Dictionary()[String(id->name)];
+ if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(subscript->attribute->name))) {
+ Variant value = base.value.operator Dictionary()[String(subscript->attribute->name)];
r_type = _type_from_variant(value);
found = true;
break;
}
const GDScriptParser::DictionaryNode *dn = nullptr;
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
- dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
- } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {
dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);
}
@@ -945,7 +1436,7 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G
if (!_guess_expression_type(c, dn->elements[i].key, key)) {
continue;
}
- if (key.value == String(id->name)) {
+ if (key.value == String(subscript->attribute->name)) {
r_type.assigned_expression = dn->elements[i].value;
found = _guess_expression_type(c, dn->elements[i].value, r_type);
break;
@@ -954,26 +1445,25 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G
}
if (!found) {
- found = _guess_identifier_type_from_base(c, base, id->name, r_type);
+ found = _guess_identifier_type_from_base(c, base, subscript->attribute->name, r_type);
}
- } break;
- case GDScriptParser::OperatorNode::OP_INDEX: {
- if (op->arguments.size() < 2) {
+ } else {
+ if (subscript->index == nullptr) {
found = false;
break;
}
- GDScriptCompletionContext c = p_context;
- c.line = op->line;
+ GDScriptParser::CompletionContext c = p_context;
+ c.current_line = subscript->start_line;
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(c, op->arguments[0], base)) {
+ if (!_guess_expression_type(c, subscript->base, base)) {
found = false;
break;
}
GDScriptCompletionIdentifier index;
- if (!_guess_expression_type(c, op->arguments[1], index)) {
+ if (!_guess_expression_type(c, subscript->index, index)) {
found = false;
break;
}
@@ -985,11 +1475,11 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G
break;
}
- // Look if it is a dictionary node
+ // Look if it is a dictionary node.
const GDScriptParser::DictionaryNode *dn = nullptr;
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_DICTIONARY) {
- dn = static_cast<const GDScriptParser::DictionaryNode *>(op->arguments[0]);
- } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_DICTIONARY) {
+ if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {
+ dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {
dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);
}
@@ -1007,13 +1497,13 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G
}
}
- // Look if it is an array node
+ // Look if it is an array node.
if (!found && index.value.is_num()) {
int idx = index.value;
const GDScriptParser::ArrayNode *an = nullptr;
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_ARRAY) {
- an = static_cast<const GDScriptParser::ArrayNode *>(op->arguments[0]);
- } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::TYPE_ARRAY) {
+ if (subscript->base->type == GDScriptParser::Node::ARRAY) {
+ an = static_cast<const GDScriptParser::ArrayNode *>(subscript->base);
+ } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::ARRAY) {
an = static_cast<const GDScriptParser::ArrayNode *>(base.assigned_expression);
}
@@ -1040,113 +1530,74 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G
found = true;
}
}
- } break;
- default: {
- if (op->arguments.size() < 2) {
- found = false;
- break;
- }
-
- Variant::Operator vop = Variant::OP_MAX;
- switch (op->op) {
- case GDScriptParser::OperatorNode::OP_ADD:
- vop = Variant::OP_ADD;
- break;
- case GDScriptParser::OperatorNode::OP_SUB:
- vop = Variant::OP_SUBTRACT;
- break;
- case GDScriptParser::OperatorNode::OP_MUL:
- vop = Variant::OP_MULTIPLY;
- break;
- case GDScriptParser::OperatorNode::OP_DIV:
- vop = Variant::OP_DIVIDE;
- break;
- case GDScriptParser::OperatorNode::OP_MOD:
- vop = Variant::OP_MODULE;
- break;
- case GDScriptParser::OperatorNode::OP_SHIFT_LEFT:
- vop = Variant::OP_SHIFT_LEFT;
- break;
- case GDScriptParser::OperatorNode::OP_SHIFT_RIGHT:
- vop = Variant::OP_SHIFT_RIGHT;
- break;
- case GDScriptParser::OperatorNode::OP_BIT_AND:
- vop = Variant::OP_BIT_AND;
- break;
- case GDScriptParser::OperatorNode::OP_BIT_OR:
- vop = Variant::OP_BIT_OR;
- break;
- case GDScriptParser::OperatorNode::OP_BIT_XOR:
- vop = Variant::OP_BIT_XOR;
- break;
- default: {
- }
- }
+ }
+ } break;
+ case GDScriptParser::Node::BINARY_OPERATOR: {
+ const GDScriptParser::BinaryOpNode *op = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
- if (vop == Variant::OP_MAX) {
- break;
- }
+ if (op->variant_op == Variant::OP_MAX) {
+ break;
+ }
- GDScriptCompletionContext context = p_context;
- context.line = op->line;
+ GDScriptParser::CompletionContext context = p_context;
+ context.current_line = op->start_line;
- GDScriptCompletionIdentifier p1;
- GDScriptCompletionIdentifier p2;
+ GDScriptCompletionIdentifier p1;
+ GDScriptCompletionIdentifier p2;
- if (!_guess_expression_type(context, op->arguments[0], p1)) {
- found = false;
- break;
- }
+ if (!_guess_expression_type(context, op->left_operand, p1)) {
+ found = false;
+ break;
+ }
- if (!_guess_expression_type(context, op->arguments[1], p2)) {
- found = false;
- break;
- }
+ if (!_guess_expression_type(context, op->right_operand, p2)) {
+ found = false;
+ break;
+ }
- Callable::CallError ce;
- bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
- Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type.builtin_type, nullptr, 0, ce);
- bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
- Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type.builtin_type, nullptr, 0, ce);
- // avoid potential invalid ops
- if ((vop == Variant::OP_DIVIDE || vop == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
- v2 = 1;
- v2_use_value = false;
- }
- if (vop == Variant::OP_DIVIDE && v2.get_type() == Variant::FLOAT) {
- v2 = 1.0;
- v2_use_value = false;
- }
+ Callable::CallError ce;
+ bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
+ Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type.builtin_type, nullptr, 0, ce);
+ bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
+ Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type.builtin_type, nullptr, 0, ce);
+ // avoid potential invalid ops
+ if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
+ v2 = 1;
+ v2_use_value = false;
+ }
+ if (op->variant_op == Variant::OP_DIVIDE && v2.get_type() == Variant::FLOAT) {
+ v2 = 1.0;
+ v2_use_value = false;
+ }
- Variant res;
- bool valid;
- Variant::evaluate(vop, v1, v2, res, valid);
- if (!valid) {
- found = false;
- break;
- }
- r_type = _type_from_variant(res);
- if (!v1_use_value || !v2_use_value) {
- r_type.value = Variant();
- r_type.type.is_constant = false;
- }
+ Variant res;
+ bool valid;
+ Variant::evaluate(op->variant_op, v1, v2, res, valid);
+ if (!valid) {
+ found = false;
+ break;
+ }
+ r_type = _type_from_variant(res);
+ if (!v1_use_value || !v2_use_value) {
+ r_type.value = Variant();
+ r_type.type.is_constant = false;
+ }
- found = true;
- } break;
- }
- } break;
- default: {
+ found = true;
+ } break;
+ default:
+ break;
}
}
// It may have found a null, but that's never useful
- if (found && r_type.type.has_type && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) {
+ if (found && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) {
found = false;
}
// Check type hint last. For collections we want chance to get the actual value first
// This way we can detect types from the content of dictionaries and arrays
- if (!found && p_expression->get_datatype().has_type) {
+ if (!found && p_expression->get_datatype().is_hard_type()) {
r_type.type = p_expression->get_datatype();
if (!r_type.assigned_expression) {
r_type.assigned_expression = p_expression;
@@ -1157,306 +1608,289 @@ static bool _guess_expression_type(GDScriptCompletionContext &p_context, const G
return found;
}
-static bool _guess_identifier_type(GDScriptCompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
- // Look in blocks first
- const GDScriptParser::BlockNode *blk = p_context.block;
+static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+ // Look in blocks first.
int last_assign_line = -1;
- const GDScriptParser::Node *last_assigned_expression = nullptr;
- GDScriptParser::DataType var_type;
- while (blk) {
- if (blk->variables.has(p_identifier)) {
- if (blk->variables[p_identifier]->line > p_context.line) {
- return false;
- }
+ const GDScriptParser::ExpressionNode *last_assigned_expression = nullptr;
+ GDScriptParser::DataType id_type;
+ GDScriptParser::SuiteNode *suite = p_context.current_suite;
+ bool is_function_parameter = false;
- var_type = blk->variables[p_identifier]->datatype;
+ if (suite) {
+ if (suite->has_local(p_identifier)) {
+ const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier);
- if (!last_assigned_expression && blk->variables[p_identifier]->assign && blk->variables[p_identifier]->assign->type == GDScriptParser::Node::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(blk->variables[p_identifier]->assign);
- if (op->op == GDScriptParser::OperatorNode::OP_ASSIGN && op->arguments.size() >= 2) {
- last_assign_line = op->line;
- last_assigned_expression = op->arguments[1];
- }
- }
- }
+ id_type = local.get_datatype();
- for (const List<GDScriptParser::Node *>::Element *E = blk->statements.front(); E; E = E->next()) {
- const GDScriptParser::Node *expr = E->get();
- if (expr->line > p_context.line || expr->type != GDScriptParser::Node::TYPE_OPERATOR) {
- continue;
+ // Check initializer as the first assignment.
+ switch (local.type) {
+ case GDScriptParser::SuiteNode::Local::VARIABLE:
+ if (local.variable->initializer) {
+ last_assign_line = local.variable->initializer->end_line;
+ last_assigned_expression = local.variable->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::CONSTANT:
+ if (local.constant->initializer) {
+ last_assign_line = local.constant->initializer->end_line;
+ last_assigned_expression = local.constant->initializer;
+ }
+ break;
+ case GDScriptParser::SuiteNode::Local::PARAMETER:
+ if (local.parameter->default_value) {
+ last_assign_line = local.parameter->default_value->end_line;
+ last_assigned_expression = local.parameter->default_value;
+ }
+ is_function_parameter = true;
+ break;
+ default:
+ break;
}
+ }
+ }
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(expr);
- if (op->op != GDScriptParser::OperatorNode::OP_ASSIGN || op->line < last_assign_line) {
- continue;
+ while (suite) {
+ for (int i = 0; i < suite->statements.size(); i++) {
+ if (suite->statements[i]->start_line > p_context.current_line) {
+ break;
}
- if (op->arguments.size() >= 2 && op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0]);
- if (id->name == p_identifier) {
- last_assign_line = op->line;
- last_assigned_expression = op->arguments[1];
- }
+ switch (suite->statements[i]->type) {
+ case GDScriptParser::Node::ASSIGNMENT: {
+ const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);
+ if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);
+ if (id->name == p_identifier) {
+ last_assign_line = assign->assigned_value->end_line;
+ last_assigned_expression = assign->assigned_value;
+ }
+ }
+ } break;
+ default:
+ // TODO: Check sub blocks (control flow statements) as they might also reassign stuff.
+ break;
}
}
- if (blk->if_condition && blk->if_condition->type == GDScriptParser::Node::TYPE_OPERATOR && static_cast<const GDScriptParser::OperatorNode *>(blk->if_condition)->op == GDScriptParser::OperatorNode::OP_IS) {
- //is used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..
- //super dirty hack, but very useful
- //credit: Zylann
- //TODO: this could be hacked to detect ANDed conditions too..
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(blk->if_condition);
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name == p_identifier) {
- //bingo
- GDScriptCompletionContext c = p_context;
- c.line = op->line;
- c.block = blk;
- if (_guess_expression_type(p_context, op->arguments[1], r_type)) {
- r_type.type.is_meta_type = false; // Right-hand of `is` will be a meta type, but the left-hand value is not
- // Not an assignment, it shouldn't carry any value
- r_type.value = Variant();
- r_type.assigned_expression = nullptr;
-
- return true;
+ if (suite->parent_if && suite->parent_if->condition && suite->parent_if->condition->type == GDScriptParser::Node::BINARY_OPERATOR && static_cast<const GDScriptParser::BinaryOpNode *>(suite->parent_if->condition)->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) {
+ // Operator `is` used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..
+ // Super dirty hack, but very useful.
+ // Credit: Zylann.
+ // TODO: this could be hacked to detect ANDed conditions too...
+ const GDScriptParser::BinaryOpNode *op = static_cast<const GDScriptParser::BinaryOpNode *>(suite->parent_if->condition);
+ if (op->left_operand && op->right_operand && op->left_operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(op->left_operand)->name == p_identifier) {
+ // Bingo.
+ GDScriptParser::CompletionContext c = p_context;
+ c.current_line = op->left_operand->start_line;
+ c.current_suite = suite;
+ GDScriptCompletionIdentifier is_type;
+ if (_guess_expression_type(c, op->right_operand, is_type)) {
+ id_type = is_type.type;
+ id_type.is_meta_type = false;
+ if (last_assign_line < c.current_line) {
+ // Override last assignment.
+ last_assign_line = c.current_line;
+ last_assigned_expression = nullptr;
+ }
}
}
}
- blk = blk->parent_block;
+ suite = suite->parent_block;
}
- if (last_assigned_expression && last_assign_line != p_context.line) {
- GDScriptCompletionContext c = p_context;
- c.line = last_assign_line;
+ if (last_assigned_expression && last_assign_line != p_context.current_line) {
+ GDScriptParser::CompletionContext c = p_context;
+ c.current_line = last_assign_line;
r_type.assigned_expression = last_assigned_expression;
if (_guess_expression_type(c, last_assigned_expression, r_type)) {
- if (var_type.has_type) {
- r_type.type = var_type;
- }
return true;
}
}
- if (var_type.has_type) {
- r_type.type = var_type;
- return true;
- }
-
- if (p_context.function) {
- for (int i = 0; i < p_context.function->arguments.size(); i++) {
- if (p_context.function->arguments[i] == p_identifier) {
- if (p_context.function->argument_types[i].has_type) {
- r_type.type = p_context.function->argument_types[i];
- return true;
- }
-
- int def_from = p_context.function->arguments.size() - p_context.function->default_values.size();
- if (i >= def_from) {
- int def_idx = i - def_from;
- if (p_context.function->default_values[def_idx]->type == GDScriptParser::Node::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_context.function->default_values[def_idx]);
- if (op->arguments.size() < 2) {
- return false;
- }
- GDScriptCompletionContext c = p_context;
- c.function = nullptr;
- c.block = nullptr;
- return _guess_expression_type(c, op->arguments[1], r_type);
- }
- }
- break;
- }
- }
-
- GDScriptParser::DataType base_type = p_context._class->base_type;
- while (base_type.has_type) {
+ if (is_function_parameter && p_context.current_function && p_context.current_class) {
+ // Check if it's override of native function, then we can assume the type from the signature.
+ GDScriptParser::DataType base_type = p_context.current_class->base_type;
+ while (base_type.is_set()) {
switch (base_type.kind) {
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<GDScript> gds = base_type.script_type;
- if (gds.is_valid() && gds->has_method(p_context.function->name)) {
- GDScriptFunction *func = gds->get_member_functions()[p_context.function->name];
- if (func) {
- for (int i = 0; i < func->get_argument_count(); i++) {
- if (func->get_argument_name(i) == p_identifier) {
- r_type = _type_from_gdtype(func->get_argument_type(i));
- return true;
- }
- }
+ case GDScriptParser::DataType::CLASS:
+ if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {
+ GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
+ const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]];
+ if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
+ id_type = parameter->get_datatype();
}
- Ref<GDScript> base_gds = gds->get_base_script();
- if (base_gds.is_valid()) {
- base_type.kind = GDScriptParser::DataType::GDSCRIPT;
- base_type.script_type = base_gds;
- } else {
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = gds->get_instance_base_type();
+ if (parameter->default_value) {
+ GDScriptParser::CompletionContext c = p_context;
+ c.current_function = parent_function;
+ c.current_class = base_type.class_type;
+ c.base = nullptr;
+ if (_guess_expression_type(c, parameter->default_value, r_type)) {
+ return true;
+ }
}
- } else {
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = gds->get_instance_base_type();
+ base_type = base_type.class_type->base_type;
}
- } break;
+ break;
case GDScriptParser::DataType::NATIVE: {
- List<MethodInfo> methods;
- ClassDB::get_method_list(base_type.native_type, &methods);
- ClassDB::get_virtual_methods(base_type.native_type, &methods);
-
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name == p_context.function->name) {
- MethodInfo &mi = E->get();
- for (List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next()) {
- if (F->get().name == p_identifier) {
- r_type = _type_from_property(F->get());
- return true;
- }
+ if (id_type.is_set() && !id_type.is_variant()) {
+ base_type = GDScriptParser::DataType();
+ break;
+ }
+ StringName real_native = _get_real_class_name(base_type.native_type);
+ MethodInfo info;
+ if (ClassDB::get_method_info(real_native, p_context.current_function->identifier->name, &info)) {
+ for (const List<PropertyInfo>::Element *E = info.arguments.front(); E; E = E->next()) {
+ if (E->get().name == p_identifier) {
+ r_type = _type_from_property(E->get());
+ return true;
}
}
}
- base_type.has_type = false;
- } break;
- default: {
- base_type.has_type = false;
+ base_type = GDScriptParser::DataType();
} break;
+ default:
+ break;
}
}
}
- // Check current class (including inheritance)
- if (p_context._class) {
- GDScriptCompletionIdentifier context_base;
- context_base.value = p_context.base;
- context_base.type.has_type = true;
- context_base.type.kind = GDScriptParser::DataType::CLASS;
- context_base.type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
- context_base.type.is_meta_type = p_context.function && p_context.function->_static;
-
- if (_guess_identifier_type_from_base(p_context, context_base, p_identifier, r_type)) {
- return true;
- }
+ if (id_type.is_set() && !id_type.is_variant()) {
+ r_type.type = id_type;
+ return true;
}
- // Check named scripts
- if (ScriptServer::is_global_class(p_identifier)) {
- Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
- if (scr.is_valid()) {
- r_type = _type_from_variant(scr);
- r_type.type.is_meta_type = true;
+ // Check current class (including inheritance).
+ if (p_context.current_class) {
+ GDScriptCompletionIdentifier base;
+ base.value = p_context.base;
+ base.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ base.type.kind = GDScriptParser::DataType::CLASS;
+ base.type.class_type = p_context.current_class;
+ base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;
+
+ if (_guess_identifier_type_from_base(p_context, base, p_identifier, r_type)) {
return true;
}
- return false;
}
- for (int i = 0; i < 2; i++) {
- StringName target_id;
- switch (i) {
- case 0:
- // Check ClassDB
- target_id = p_identifier;
- break;
- case 1:
- // ClassDB again for underscore-prefixed classes
- target_id = String("_") + p_identifier;
- break;
- }
-
- if (ClassDB::class_exists(target_id)) {
- r_type.type.has_type = true;
- r_type.type.kind = GDScriptParser::DataType::NATIVE;
- r_type.type.native_type = target_id;
- if (Engine::get_singleton()->has_singleton(target_id)) {
- r_type.type.is_meta_type = false;
- r_type.value = Engine::get_singleton()->get_singleton_object(target_id);
- } else {
+ // Check global scripts.
+ if (ScriptServer::is_global_class(p_identifier)) {
+ String script = ScriptServer::get_global_class_path(p_identifier);
+ if (script.to_lower().ends_with(".gd")) {
+ Error err = OK;
+ Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
+ if (err == OK) {
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.script_path = script;
+ r_type.type.class_type = parser->get_parser()->get_tree();
+ r_type.type.is_constant = false;
+ r_type.type.kind = GDScriptParser::DataType::CLASS;
+ r_type.value = Variant();
+ p_context.dependent_parsers.push_back(parser);
+ return true;
+ }
+ } else {
+ Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
+ if (scr.is_valid()) {
+ r_type = _type_from_variant(scr);
r_type.type.is_meta_type = true;
- const Map<StringName, int>::Element *target_elem = GDScriptLanguage::get_singleton()->get_global_map().find(target_id);
- // Check because classes like EditorNode are in ClassDB by now, but unknown to GDScript
- if (!target_elem) {
- return false;
- }
- int idx = target_elem->get();
- r_type.value = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ return true;
}
- return true;
}
+ return false;
}
- // Check autoload singletons
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
+ // Check autoloads.
+ if (ProjectSettings::get_singleton()->has_autoload(p_identifier)) {
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
return true;
}
+ // Check ClassDB.
+ StringName class_name = _get_real_class_name(p_identifier);
+ if (ClassDB::class_exists(class_name) && ClassDB::is_class_exposed(class_name)) {
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.kind = GDScriptParser::DataType::NATIVE;
+ r_type.type.native_type = p_identifier;
+ r_type.type.is_constant = true;
+ r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(class_name);
+ r_type.value = Variant();
+ }
+
return false;
}
-static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
GDScriptParser::DataType base_type = p_base.type;
- bool _static = base_type.is_meta_type;
- while (base_type.has_type) {
+ bool is_static = base_type.is_meta_type;
+ while (base_type.is_set()) {
switch (base_type.kind) {
- case GDScriptParser::DataType::CLASS: {
- if (base_type.class_type->constant_expressions.has(p_identifier)) {
- GDScriptParser::ClassNode::Constant c = base_type.class_type->constant_expressions[p_identifier];
- r_type.type = c.type;
- if (c.expression->type == GDScriptParser::Node::TYPE_CONSTANT) {
- r_type.value = static_cast<const GDScriptParser::ConstantNode *>(c.expression)->value;
- }
- return true;
- }
-
- if (!_static) {
- for (int i = 0; i < base_type.class_type->variables.size(); i++) {
- GDScriptParser::ClassNode::Member m = base_type.class_type->variables[i];
- if (m.identifier == p_identifier) {
- if (m.expression) {
- if (p_context.line == m.expression->line) {
- // Variable used in the same expression
- return false;
- }
-
- if (_guess_expression_type(p_context, m.expression, r_type)) {
- return true;
- }
- if (m.expression->get_datatype().has_type) {
- r_type.type = m.expression->get_datatype();
+ case GDScriptParser::DataType::CLASS:
+ if (base_type.class_type->has_member(p_identifier)) {
+ const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_identifier);
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::CONSTANT:
+ r_type.type = member.constant->get_datatype();
+ if (member.constant->initializer && member.constant->initializer->is_constant) {
+ r_type.value = member.constant->initializer->reduced_value;
+ }
+ return true;
+ case GDScriptParser::ClassNode::Member::VARIABLE:
+ if (!is_static) {
+ if (member.variable->initializer) {
+ const GDScriptParser::ExpressionNode *init = member.variable->initializer;
+ if (init->is_constant) {
+ r_type.value = init->reduced_value;
+ r_type = _type_from_variant(init->reduced_value);
+ return true;
+ } else if (init->start_line == p_context.current_line) {
+ return false;
+ } else if (_guess_expression_type(p_context, init, r_type)) {
+ return true;
+ } else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) {
+ r_type.type = init->get_datatype();
+ return true;
+ }
+ } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {
+ r_type.type = member.variable->get_datatype();
return true;
}
}
- if (m.data_type.has_type) {
- r_type.type = m.data_type;
- return true;
- }
+ // TODO: Check assignments in constructor.
return false;
- }
- }
- }
- base_type = base_type.class_type->base_type;
- } break;
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<GDScript> gds = base_type.script_type;
- if (gds.is_valid()) {
- if (gds->get_constants().has(p_identifier)) {
- r_type = _type_from_variant(gds->get_constants()[p_identifier]);
- return true;
- }
- if (!_static) {
- const Set<StringName>::Element *m = gds->get_members().find(p_identifier);
- if (m) {
- r_type = _type_from_gdtype(gds->get_member_type(p_identifier));
+ case GDScriptParser::ClassNode::Member::ENUM:
+ r_type.type = member.m_enum->get_datatype();
+ r_type.enumeration = member.m_enum->identifier->name;
return true;
- }
- }
- Ref<GDScript> parent = gds->get_base_script();
- if (parent.is_valid()) {
- base_type.script_type = parent;
- } else {
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = gds->get_instance_base_type();
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE:
+ r_type = _type_from_variant(member.enum_value.value);
+ return true;
+ case GDScriptParser::ClassNode::Member::SIGNAL:
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::SIGNAL;
+ return true;
+ case GDScriptParser::ClassNode::Member::FUNCTION:
+ if (is_static && !member.function->is_static) {
+ return false;
+ }
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.kind = GDScriptParser::DataType::BUILTIN;
+ r_type.type.builtin_type = Variant::CALLABLE;
+ return true;
+ case GDScriptParser::ClassNode::Member::CLASS:
+ r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_type.type.kind = GDScriptParser::DataType::CLASS;
+ r_type.type.class_type = member.m_class;
+ return true;
+ case GDScriptParser::ClassNode::Member::UNDEFINED:
+ return false; // Unreachable.
}
- } else {
return false;
}
- } break;
+ base_type = base_type.class_type->base_type;
+ break;
case GDScriptParser::DataType::SCRIPT: {
Ref<Script> scr = base_type.script_type;
if (scr.is_valid()) {
@@ -1467,7 +1901,7 @@ static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_contex
return true;
}
- if (!_static) {
+ if (!is_static) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
for (const List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
@@ -1490,33 +1924,25 @@ static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_contex
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = base_type.native_type;
+ StringName class_name = _get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(class_name)) {
- class_name = String("_") + class_name;
- if (!ClassDB::class_exists(class_name)) {
- return false;
- }
+ return false;
}
- // Skip constants since they're all integers. Type does not matter because int has no members
+ // Skip constants since they're all integers. Type does not matter because int has no members.
- List<PropertyInfo> props;
- ClassDB::get_property_list(class_name, &props);
- for (const List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- const PropertyInfo &prop = E->get();
- if (prop.name == p_identifier) {
- StringName getter = ClassDB::get_property_getter(class_name, p_identifier);
- if (getter != StringName()) {
- MethodBind *g = ClassDB::get_method(class_name, getter);
- if (g) {
- r_type = _type_from_property(g->get_return_info());
- return true;
- }
- } else {
- r_type = _type_from_property(prop);
+ PropertyInfo prop;
+ if (ClassDB::get_property_info(class_name, p_identifier, &prop)) {
+ StringName getter = ClassDB::get_property_getter(class_name, p_identifier);
+ if (getter != StringName()) {
+ MethodBind *g = ClassDB::get_method(class_name, getter);
+ if (g) {
+ r_type = _type_from_property(g->get_return_info());
return true;
}
- break;
+ } else {
+ r_type = _type_from_property(prop);
+ return true;
}
}
return false;
@@ -1543,116 +1969,99 @@ static bool _guess_identifier_type_from_base(GDScriptCompletionContext &p_contex
} break;
}
}
-
return false;
}
-static bool _find_last_return_in_block(const GDScriptCompletionContext &p_context, int &r_last_return_line, const GDScriptParser::Node **r_last_returned_value) {
- if (!p_context.block) {
- return false;
+static void _find_last_return_in_block(GDScriptParser::CompletionContext &p_context, int &r_last_return_line, const GDScriptParser::ExpressionNode **r_last_returned_value) {
+ if (!p_context.current_suite) {
+ return;
}
- for (int i = 0; i < p_context.block->statements.size(); i++) {
- if (p_context.block->statements[i]->line < r_last_return_line) {
- continue;
- }
- if (p_context.block->statements[i]->type != GDScriptParser::Node::TYPE_CONTROL_FLOW) {
- continue;
+ for (int i = 0; i < p_context.current_suite->statements.size(); i++) {
+ if (p_context.current_suite->statements[i]->start_line < r_last_return_line) {
+ break;
}
- const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(p_context.block->statements[i]);
- if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_RETURN && cf->arguments.size() > 0) {
- if (cf->line > r_last_return_line) {
- r_last_return_line = cf->line;
- *r_last_returned_value = cf->arguments[0];
- }
+ GDScriptParser::CompletionContext c = p_context;
+ switch (p_context.current_suite->statements[i]->type) {
+ case GDScriptParser::Node::FOR:
+ c.current_suite = static_cast<const GDScriptParser::ForNode *>(p_context.current_suite->statements[i])->loop;
+ _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
+ break;
+ case GDScriptParser::Node::WHILE:
+ c.current_suite = static_cast<const GDScriptParser::WhileNode *>(p_context.current_suite->statements[i])->loop;
+ _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
+ break;
+ case GDScriptParser::Node::IF: {
+ const GDScriptParser::IfNode *_if = static_cast<const GDScriptParser::IfNode *>(p_context.current_suite->statements[i]);
+ c.current_suite = _if->true_block;
+ _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
+ if (_if->false_block) {
+ c.current_suite = _if->false_block;
+ _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
+ }
+ } break;
+ case GDScriptParser::Node::MATCH: {
+ const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(p_context.current_suite->statements[i]);
+ for (int j = 0; j < match->branches.size(); j++) {
+ c.current_suite = match->branches[j]->block;
+ _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
+ }
+ } break;
+ case GDScriptParser::Node::RETURN: {
+ const GDScriptParser::ReturnNode *ret = static_cast<const GDScriptParser::ReturnNode *>(p_context.current_suite->statements[i]);
+ if (ret->return_value) {
+ if (ret->start_line > r_last_return_line) {
+ r_last_return_line = ret->start_line;
+ *r_last_returned_value = ret->return_value;
+ }
+ }
+ } break;
+ default:
+ break;
}
}
-
- // Recurse into subblocks
- for (int i = 0; i < p_context.block->sub_blocks.size(); i++) {
- GDScriptCompletionContext c = p_context;
- c.block = p_context.block->sub_blocks[i];
- _find_last_return_in_block(c, r_last_return_line, r_last_returned_value);
- }
-
- return false;
}
-static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) {
+static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) {
GDScriptParser::DataType base_type = p_base.type;
- bool _static = base_type.is_meta_type;
+ bool is_static = base_type.is_meta_type;
- if (_static && p_method == "new") {
+ if (is_static && p_method == "new") {
r_type.type = base_type;
r_type.type.is_meta_type = false;
r_type.type.is_constant = false;
return true;
}
- while (base_type.has_type) {
+ while (base_type.is_set() && !base_type.is_variant()) {
switch (base_type.kind) {
- case GDScriptParser::DataType::CLASS: {
- if (!base_type.class_type) {
- base_type.has_type = false;
- break;
- }
-
- for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
- if (base_type.class_type->static_functions[i]->name == p_method) {
+ case GDScriptParser::DataType::CLASS:
+ if (base_type.class_type->has_function(p_method)) {
+ const GDScriptParser::FunctionNode *method = base_type.class_type->get_member(p_method).function;
+ if (!is_static || method->is_static) {
int last_return_line = -1;
- const GDScriptParser::Node *last_returned_value = nullptr;
- GDScriptCompletionContext c = p_context;
- c._class = base_type.class_type;
- c.function = base_type.class_type->static_functions[i];
- c.block = c.function->body;
+ const GDScriptParser::ExpressionNode *last_returned_value = nullptr;
+ GDScriptParser::CompletionContext c = p_context;
+ c.current_class = base_type.class_type;
+ c.current_function = const_cast<GDScriptParser::FunctionNode *>(method);
+ c.current_suite = method->body;
_find_last_return_in_block(c, last_return_line, &last_returned_value);
if (last_returned_value) {
- c.line = c.block->end_line;
- return _guess_expression_type(c, last_returned_value, r_type);
- }
- }
- }
- if (!_static) {
- for (int i = 0; i < base_type.class_type->functions.size(); i++) {
- if (base_type.class_type->functions[i]->name == p_method) {
- int last_return_line = -1;
- const GDScriptParser::Node *last_returned_value = nullptr;
- GDScriptCompletionContext c = p_context;
- c._class = base_type.class_type;
- c.function = base_type.class_type->functions[i];
- c.block = c.function->body;
-
- _find_last_return_in_block(c, last_return_line, &last_returned_value);
- if (last_returned_value) {
- c.line = c.block->end_line;
- return _guess_expression_type(c, last_returned_value, r_type);
+ c.current_line = c.current_suite->end_line;
+ if (_guess_expression_type(c, last_returned_value, r_type)) {
+ return true;
+ }
+ if (method->get_datatype().is_set() && !method->get_datatype().is_variant()) {
+ r_type.type = method->get_datatype();
+ return true;
}
}
}
}
-
base_type = base_type.class_type->base_type;
- } break;
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<GDScript> gds = base_type.script_type;
- if (gds.is_valid()) {
- if (gds->get_member_functions().has(p_method)) {
- r_type = _type_from_gdtype(gds->get_member_functions()[p_method]->get_return_type());
- return true;
- }
- Ref<GDScript> base_script = gds->get_base_script();
- if (base_script.is_valid()) {
- base_type.script_type = base_script;
- } else {
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = gds->get_instance_base_type();
- }
- } else {
- return false;
- }
- } break;
+ break;
case GDScriptParser::DataType::SCRIPT: {
Ref<Script> scr = base_type.script_type;
if (scr.is_valid()) {
@@ -1677,12 +2086,9 @@ static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_con
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName native = base_type.native_type;
+ StringName native = _get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(native)) {
- native = String("_") + native;
- if (!ClassDB::class_exists(native)) {
- return false;
- }
+ return false;
}
MethodBind *mb = ClassDB::get_method(native, p_method);
if (mb) {
@@ -1715,103 +2121,27 @@ static bool _guess_method_return_type_from_base(GDScriptCompletionContext &p_con
}
}
}
- return false;
-}
-
-static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx) {
- String arghint = _get_visual_datatype(p_info.return_val, false) + " " + p_info.name + "(";
-
- int def_args = p_info.arguments.size() - p_info.default_arguments.size();
- int i = 0;
- for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E; E = E->next()) {
- if (i > 0) {
- arghint += ", ";
- }
-
- if (i == p_arg_idx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += E->get().name + ": " + _get_visual_datatype(E->get(), true);
-
- if (i - def_args >= 0) {
- arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();
- }
-
- if (i == p_arg_idx) {
- arghint += String::chr(0xFFFF);
- }
-
- i++;
- }
-
- if (p_info.flags & METHOD_FLAG_VARARG) {
- if (p_info.arguments.size() > 0) {
- arghint += ", ";
- }
- if (p_arg_idx >= p_info.arguments.size()) {
- arghint += String::chr(0xFFFF);
- }
- arghint += "...";
- if (p_arg_idx >= p_info.arguments.size()) {
- arghint += String::chr(0xFFFF);
- }
- }
-
- arghint += ")";
-
- return arghint;
-}
-
-static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx) {
- String arghint = p_function->return_type.to_string() + " " + p_function->name.operator String() + "(";
-
- int def_args = p_function->arguments.size() - p_function->default_values.size();
- for (int i = 0; i < p_function->arguments.size(); i++) {
- if (i > 0) {
- arghint += ", ";
- }
- if (i == p_arg_idx) {
- arghint += String::chr(0xFFFF);
- }
- arghint += p_function->arguments[i].operator String() + ": " + p_function->argument_types[i].to_string();
-
- if (i - def_args >= 0) {
- String def_val = "<unknown>";
- if (p_function->default_values[i - def_args] && p_function->default_values[i - def_args]->type == GDScriptParser::Node::TYPE_OPERATOR) {
- const GDScriptParser::OperatorNode *assign = static_cast<const GDScriptParser::OperatorNode *>(p_function->default_values[i - def_args]);
-
- if (assign->arguments.size() >= 2) {
- if (assign->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT) {
- const GDScriptParser::ConstantNode *cn = static_cast<const GDScriptParser::ConstantNode *>(assign->arguments[1]);
- def_val = cn->value.get_construct_string();
- } else if (assign->arguments[1]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->arguments[1]);
- def_val = id->name.operator String();
- }
- }
- }
- arghint += " = " + def_val;
- }
- if (i == p_arg_idx) {
- arghint += String::chr(0xFFFF);
- }
- }
-
- arghint += ")";
-
- return arghint;
+ return false;
}
-static void _find_enumeration_candidates(const String p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) {
+static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) {
if (p_enum_hint.find(".") == -1) {
- // Global constant
+ // Global constant or in the current class.
StringName current_enum = p_enum_hint;
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- ScriptCodeCompletionOption option(GlobalConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
+ if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) {
+ const GDScriptParser::EnumNode *_enum = p_context.current_class->get_member(current_enum).m_enum;
+ for (int i = 0; i < _enum->values.size(); i++) {
+ ScriptCodeCompletionOption option(_enum->values[i].identifier->name, ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
+ } else {
+ for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
+ if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
+ ScriptCodeCompletionOption option(GlobalConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
+ r_result.insert(option.display, option);
+ }
+ }
}
} else {
String class_name = p_enum_hint.get_slice(".", 0);
@@ -1831,496 +2161,60 @@ static void _find_enumeration_candidates(const String p_enum_hint, Map<String, S
}
}
-static void _find_identifiers_in_block(const GDScriptCompletionContext &p_context, Map<String, ScriptCodeCompletionOption> &r_result) {
- for (Map<StringName, GDScriptParser::LocalVarNode *>::Element *E = p_context.block->variables.front(); E; E = E->next()) {
- if (E->get()->line < p_context.line) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_VARIABLE);
- r_result.insert(option.display, option);
- }
- }
- if (p_context.block->parent_block) {
- GDScriptCompletionContext c = p_context;
- c.block = p_context.block->parent_block;
- _find_identifiers_in_block(c, r_result);
- }
-}
-
-static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result);
-
-static void _find_identifiers_in_class(const GDScriptCompletionContext &p_context, bool p_static, bool p_only_functions, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result) {
- if (!p_parent_only) {
- if (!p_static && !p_only_functions) {
- for (int i = 0; i < p_context._class->variables.size(); i++) {
- ScriptCodeCompletionOption option(p_context._class->variables[i].identifier, ScriptCodeCompletionOption::KIND_MEMBER);
- r_result.insert(option.display, option);
- }
- }
-
- if (!p_only_functions) {
- for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_context._class->constant_expressions.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_CONSTANT);
- r_result.insert(option.display, option);
- }
- for (int i = 0; i < p_context._class->subclasses.size(); i++) {
- ScriptCodeCompletionOption option(p_context._class->subclasses[i]->name, ScriptCodeCompletionOption::KIND_CLASS);
- r_result.insert(option.display, option);
- }
- }
-
- for (int i = 0; i < p_context._class->static_functions.size(); i++) {
- ScriptCodeCompletionOption option(p_context._class->static_functions[i]->name.operator String(), ScriptCodeCompletionOption::KIND_FUNCTION);
- if (p_context._class->static_functions[i]->arguments.size()) {
- option.insert_text += "(";
- } else {
- option.insert_text += "()";
- }
- r_result.insert(option.display, option);
- }
-
- if (!p_static) {
- for (int i = 0; i < p_context._class->functions.size(); i++) {
- ScriptCodeCompletionOption option(p_context._class->functions[i]->name.operator String(), ScriptCodeCompletionOption::KIND_FUNCTION);
- if (p_context._class->functions[i]->arguments.size()) {
- option.insert_text += "(";
- } else {
- option.insert_text += "()";
- }
- r_result.insert(option.display, option);
- }
- }
- }
-
- // Parents
- GDScriptCompletionIdentifier base_type;
- base_type.type = p_context._class->base_type;
- base_type.type.is_meta_type = p_static;
- base_type.value = p_context.base;
-
- GDScriptCompletionContext c = p_context;
- c.block = nullptr;
- c.function = nullptr;
-
- _find_identifiers_in_base(c, base_type, p_only_functions, r_result);
-}
-
-static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) {
- GDScriptParser::DataType base_type = p_base.type;
- bool _static = base_type.is_meta_type;
-
- if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) {
- ScriptCodeCompletionOption option("new", ScriptCodeCompletionOption::KIND_FUNCTION);
- option.insert_text += "(";
- r_result.insert(option.display, option);
- }
-
- while (base_type.has_type) {
- switch (base_type.kind) {
- case GDScriptParser::DataType::CLASS: {
- GDScriptCompletionContext c = p_context;
- c._class = base_type.class_type;
- c.block = nullptr;
- c.function = nullptr;
- _find_identifiers_in_class(c, _static, p_only_functions, false, r_result);
- base_type = base_type.class_type->base_type;
- } break;
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<GDScript> script = base_type.script_type;
- if (script.is_valid()) {
- if (!_static && !p_only_functions) {
- if (p_context.base && p_context.base->get_script_instance()) {
- List<PropertyInfo> members;
- p_context.base->get_script_instance()->get_property_list(&members);
- for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
- r_result.insert(option.display, option);
- }
- }
- for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER);
- r_result.insert(option.display, option);
- }
- }
- if (!p_only_functions) {
- for (const Map<StringName, Variant>::Element *E = script->get_constants().front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
- r_result.insert(option.display, option);
- }
- }
- for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
- if (!_static || E->get()->is_static()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get()->get_argument_count()) {
- option.insert_text += "(";
- } else {
- option.insert_text += "()";
- }
- r_result.insert(option.display, option);
- }
- }
- if (!p_only_functions) {
- for (const Map<StringName, Ref<GDScript>>::Element *E = script->get_subclasses().front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- r_result.insert(option.display, option);
- }
- }
- base_type = GDScriptParser::DataType();
- if (script->get_base().is_valid()) {
- base_type.has_type = true;
- base_type.kind = GDScriptParser::DataType::GDSCRIPT;
- base_type.script_type = script->get_base();
- } else {
- base_type.has_type = script->get_instance_base_type() != StringName();
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = script->get_instance_base_type();
- }
- } else {
- return;
- }
- } break;
- case GDScriptParser::DataType::SCRIPT: {
- Ref<Script> scr = base_type.script_type;
- if (scr.is_valid()) {
- if (!_static && !p_only_functions) {
- List<PropertyInfo> members;
- scr->get_script_property_list(&members);
- for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
- r_result.insert(option.display, option);
- }
- }
- if (!p_only_functions) {
- Map<StringName, Variant> constants;
- scr->get_constants(&constants);
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
- r_result.insert(option.display, option);
- }
- }
-
- List<MethodInfo> methods;
- scr->get_script_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
- option.insert_text += "(";
- } else {
- option.insert_text += "()";
- }
- r_result.insert(option.display, option);
- }
-
- Ref<Script> base_script = scr->get_base_script();
- if (base_script.is_valid()) {
- base_type.script_type = base_script;
- } else {
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = scr->get_instance_base_type();
- }
- } else {
- return;
- }
- } break;
- case GDScriptParser::DataType::NATIVE: {
- StringName type = base_type.native_type;
- if (!ClassDB::class_exists(type)) {
- type = String("_") + type;
- if (!ClassDB::class_exists(type)) {
- return;
- }
- }
-
- if (!p_only_functions) {
- List<String> constants;
- ClassDB::get_integer_constant_list(type, &constants);
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
- r_result.insert(option.display, option);
- }
-
- if (!_static) {
- List<PropertyInfo> pinfo;
- ClassDB::get_property_list(type, &pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_CATEGORY)) {
- continue;
- }
- if (E->get().name.find("/") != -1) {
- continue;
- }
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
- r_result.insert(option.display, option);
- }
- }
- }
-
- if (!_static) {
- List<MethodInfo> methods;
- bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize();
- ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("_")) {
- continue;
- }
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
- option.insert_text += "(";
- } else {
- option.insert_text += "()";
- }
- r_result.insert(option.display, option);
- }
- }
-
- return;
- } break;
- case GDScriptParser::DataType::BUILTIN: {
- Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- return;
- }
-
- if (!p_only_functions) {
- List<PropertyInfo> members;
- tmp.get_property_list(&members);
-
- for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- if (String(E->get().name).find("/") == -1) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
- r_result.insert(option.display, option);
- }
- }
- }
-
- List<MethodInfo> methods;
- tmp.get_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
- option.insert_text += "(";
- } else {
- option.insert_text += "()";
- }
- r_result.insert(option.display, option);
- }
-
- return;
- } break;
- default: {
- return;
- } break;
- }
- }
-}
-
-static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) {
- const GDScriptParser::BlockNode *block = p_context.block;
-
- if (p_context.function) {
- const GDScriptParser::FunctionNode *f = p_context.function;
-
- for (int i = 0; i < f->arguments.size(); i++) {
- ScriptCodeCompletionOption option(f->arguments[i].operator String(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- r_result.insert(option.display, option);
- }
- }
-
- if (!p_only_functions && block) {
- GDScriptCompletionContext c = p_context;
- c.block = block;
- _find_identifiers_in_block(c, r_result);
- }
-
- const GDScriptParser::ClassNode *clss = p_context._class;
- bool _static = p_context.function && p_context.function->_static;
-
- while (clss) {
- GDScriptCompletionContext c = p_context;
- c._class = clss;
- c.block = nullptr;
- c.function = nullptr;
- _find_identifiers_in_class(c, _static, p_only_functions, false, r_result);
- _static = true;
- clss = clss->owner;
- }
-
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- MethodInfo mi = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
- ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION);
- if (mi.arguments.size() || (mi.flags & METHOD_FLAG_VARARG)) {
- option.insert_text += "(";
- } else {
- option.insert_text += "()";
- }
- r_result.insert(option.display, option);
- }
-
- static const char *_type_names[Variant::VARIANT_MAX] = {
- "null", "bool", "int", "float", "String", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform",
- "Color", "StringName", "NodePath", "RID", "Object", "Callable", "Signal", "Dictionary", "Array", "PackedByteArray", "PackedInt32Array", "PackedInt64Array", "PackedFloat32Array", "PackedFloat64Array", "PackedStringArray",
- "PackedVector2Array", "PackedVector3Array", "PackedColorArray"
- };
-
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- ScriptCodeCompletionOption option(_type_names[i], ScriptCodeCompletionOption::KIND_CLASS);
- r_result.insert(option.display, option);
- }
-
- static const char *_keywords[] = {
- "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
- "breakpoint", "class", "extends", "is", "func", "preload", "setget", "signal", "tool", "yield",
- "const", "enum", "export", "onready", "static", "var", "break", "continue", "if", "elif",
- "else", "for", "pass", "return", "match", "while", "remote", "master", "puppet",
- "remotesync", "mastersync", "puppetsync",
- nullptr
- };
-
- const char **kw = _keywords;
- while (*kw) {
- ScriptCodeCompletionOption option(*kw, ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- r_result.insert(option.display, option);
- kw++;
- }
-
- // Autoload singletons
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
- }
- String path = ProjectSettings::get_singleton()->get(s);
- if (path.begins_with("*")) {
- ScriptCodeCompletionOption option(s.get_slice("/", 1), ScriptCodeCompletionOption::KIND_CONSTANT);
- r_result.insert(option.display, option);
- }
- }
-
- // Named scripts
- List<StringName> named_scripts;
- ScriptServer::get_global_class_list(&named_scripts);
- for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- r_result.insert(option.display, option);
- }
-
- // Native classes
- for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- r_result.insert(option.display, option);
- }
-}
-
-static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Map<String, ScriptCodeCompletionOption> &r_result, String &r_arghint) {
+static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Map<String, ScriptCodeCompletionOption> &r_result, String &r_arghint) {
Variant base = p_base.value;
GDScriptParser::DataType base_type = p_base.type;
const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
-#define IS_METHOD_SIGNAL(m_method) (m_method == "connect" || m_method == "disconnect" || m_method == "is_connected" || m_method == "emit_signal")
-
- while (base_type.has_type) {
+ while (base_type.is_set() && !base_type.is_variant()) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
- for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
- if (base_type.class_type->static_functions[i]->name == p_method) {
- r_arghint = _make_arguments_hint(base_type.class_type->static_functions[i], p_argidx);
- return;
- }
- }
- for (int i = 0; i < base_type.class_type->functions.size(); i++) {
- if (base_type.class_type->functions[i]->name == p_method) {
- r_arghint = _make_arguments_hint(base_type.class_type->functions[i], p_argidx);
- return;
- }
- }
+ if (base_type.class_type->has_member(p_method)) {
+ const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_method);
- if (IS_METHOD_SIGNAL(p_method) && p_argidx == 0) {
- for (int i = 0; i < base_type.class_type->_signals.size(); i++) {
- ScriptCodeCompletionOption option(base_type.class_type->_signals[i].name.operator String(), ScriptCodeCompletionOption::KIND_SIGNAL);
- option.insert_text = quote_style + option.display + quote_style;
- r_result.insert(option.display, option);
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
+ r_arghint = _make_arguments_hint(member.function, p_argidx);
+ return;
}
}
base_type = base_type.class_type->base_type;
} break;
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<GDScript> gds = base_type.script_type;
- if (gds.is_valid()) {
- if (IS_METHOD_SIGNAL(p_method) && p_argidx == 0) {
- List<MethodInfo> signals;
- gds->get_script_signal_list(&signals);
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL);
- option.insert_text = quote_style + option.display + quote_style;
- r_result.insert(option.display, option);
- }
- }
- Ref<GDScript> base_script = gds->get_base_script();
- if (base_script.is_valid()) {
- base_type.script_type = base_script;
- } else {
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = gds->get_instance_base_type();
- }
- } else {
- return;
- }
- } break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = base_type.native_type;
+ StringName class_name = _get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(class_name)) {
- class_name = String("_") + class_name;
- if (!ClassDB::class_exists(class_name)) {
- base_type.has_type = false;
- break;
- }
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
+ break;
}
- List<MethodInfo> methods;
- ClassDB::get_method_list(class_name, &methods);
- ClassDB::get_virtual_methods(class_name, &methods);
+ MethodInfo info;
int method_args = 0;
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name == p_method) {
- method_args = E->get().arguments.size();
- if (base.get_type() == Variant::OBJECT) {
- Object *obj = base.operator Object *();
- if (obj) {
- List<String> options;
- obj->get_argument_options(p_method, p_argidx, &options);
- for (List<String>::Element *F = options.front(); F; F = F->next()) {
- ScriptCodeCompletionOption option(F->get(), ScriptCodeCompletionOption::KIND_FUNCTION);
- r_result.insert(option.display, option);
- }
+ if (ClassDB::get_method_info(class_name, p_method, &info)) {
+ method_args = info.arguments.size();
+ if (base.get_type() == Variant::OBJECT) {
+ Object *obj = base.operator Object *();
+ if (obj) {
+ List<String> options;
+ obj->get_argument_options(p_method, p_argidx, &options);
+ for (List<String>::Element *F = options.front(); F; F = F->next()) {
+ ScriptCodeCompletionOption option(F->get(), ScriptCodeCompletionOption::KIND_FUNCTION);
+ r_result.insert(option.display, option);
}
}
+ }
- if (p_argidx < method_args) {
- PropertyInfo arg_info = E->get().arguments[p_argidx];
- if (arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
- _find_enumeration_candidates(arg_info.class_name, r_result);
- }
+ if (p_argidx < method_args) {
+ PropertyInfo arg_info = info.arguments[p_argidx];
+ if (arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ _find_enumeration_candidates(p_context, arg_info.class_name, r_result);
}
-
- r_arghint = _make_arguments_hint(E->get(), p_argidx);
- break;
}
- }
- if (IS_METHOD_SIGNAL(p_method) && p_argidx == 0) {
- List<MethodInfo> signals;
- ClassDB::get_signal_list(class_name, &signals);
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL);
- option.insert_text = quote_style + option.display + quote_style;
- r_result.insert(option.display, option);
- }
+ r_arghint = _make_arguments_hint(info, p_argidx);
+ return;
}
-#undef IS_METHOD_SIGNAL
if (ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node") && p_argidx == 0) {
// Get autoloads
@@ -2355,7 +2249,7 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con
}
}
- base_type.has_type = false;
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
} break;
case GDScriptParser::DataType::BUILTIN: {
if (base.get_type() == Variant::NIL) {
@@ -2375,139 +2269,93 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con
}
}
- base_type.has_type = false;
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
} break;
default: {
- base_type.has_type = false;
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
} break;
}
}
}
-static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Map<String, ScriptCodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {
+static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, Map<String, ScriptCodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {
const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
- if (!p_node || p_node->type != GDScriptParser::Node::TYPE_OPERATOR) {
+ if (p_call->type == GDScriptParser::Node::PRELOAD) {
+ if (p_argidx == 0 && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
+ }
+
+ MethodInfo mi(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), "preload", PropertyInfo(Variant::STRING, "path"));
+ r_arghint = _make_arguments_hint(mi, p_argidx);
+ return;
+ } else if (p_call->type != GDScriptParser::Node::CALL) {
return;
}
Variant base;
GDScriptParser::DataType base_type;
- StringName function;
bool _static = false;
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(p_node);
+ const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call);
+ GDScriptParser::Node::Type callee_type = GDScriptParser::Node::NONE;
GDScriptCompletionIdentifier connect_base;
- if (op->op != GDScriptParser::OperatorNode::OP_CALL && op->op != GDScriptParser::OperatorNode::OP_PARENT_CALL) {
- return;
- }
+ if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
+ MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
- if (!op->arguments.size()) {
- return;
- }
-
- if (op->op == GDScriptParser::OperatorNode::OP_CALL) {
- if (op->arguments[0]->type == GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION) {
- // Complete built-in function
- const GDScriptParser::BuiltInFunctionNode *fn = static_cast<const GDScriptParser::BuiltInFunctionNode *>(op->arguments[0]);
- MethodInfo mi = GDScriptFunctions::get_info(fn->function);
-
- if ((mi.name == "load" || mi.name == "preload") && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
- _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
- }
-
- r_arghint = _make_arguments_hint(mi, p_argidx);
- return;
-
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_TYPE) {
- // Complete constructor
- const GDScriptParser::TypeNode *tn = static_cast<const GDScriptParser::TypeNode *>(op->arguments[0]);
-
- List<MethodInfo> constructors;
- Variant::get_constructor_list(tn->vtype, &constructors);
+ if ((info.name == "load" || info.name == "preload") && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
+ }
- int i = 0;
- for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
- if (p_argidx >= E->get().arguments.size()) {
- continue;
- }
- if (i > 0) {
- r_arghint += "\n";
- }
- r_arghint += _make_arguments_hint(E->get(), p_argidx);
- i++;
- }
- return;
- } else if (op->arguments[0]->type == GDScriptParser::Node::TYPE_SELF) {
- if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
- return;
+ r_arghint = _make_arguments_hint(info, p_argidx);
+ return;
+ } else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
+ // Complete constructor
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);
+
+ int i = 0;
+ for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
+ if (p_argidx >= E->get().arguments.size()) {
+ continue;
}
-
- base = p_context.base;
-
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
- function = id->name;
- base_type.has_type = true;
- base_type.kind = GDScriptParser::DataType::CLASS;
- base_type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
- _static = p_context.function && p_context.function->_static;
-
- if (function == "connect" && op->arguments.size() >= 4) {
- _guess_expression_type(p_context, op->arguments[3], connect_base);
+ if (i > 0) {
+ r_arghint += "\n";
}
+ r_arghint += _make_arguments_hint(E->get(), p_argidx);
+ i++;
+ }
+ return;
+ } else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) {
+ base = p_context.base;
- } else {
- if (op->arguments.size() < 2 || op->arguments[1]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
- return;
- }
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[1]);
- function = id->name;
+ if (p_context.current_class) {
+ base_type = p_context.current_class->get_datatype();
+ _static = !p_context.current_function || p_context.current_function->is_static;
+ }
+ } else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
+ if (subscript->is_attribute) {
GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(p_context, op->arguments[0], ci)) {
+ if (_guess_expression_type(p_context, subscript->base, ci)) {
base_type = ci.type;
base = ci.value;
} else {
return;
}
- _static = ci.type.is_meta_type;
- if (function == "connect" && op->arguments.size() >= 4) {
- _guess_expression_type(p_context, op->arguments[3], connect_base);
- }
+ _static = base_type.is_meta_type;
}
} else {
- if (!p_context._class || op->arguments.size() < 1 || op->arguments[0]->type != GDScriptParser::Node::TYPE_IDENTIFIER) {
- return;
- }
- base_type.has_type = true;
- base_type.kind = GDScriptParser::DataType::CLASS;
- base_type.class_type = const_cast<GDScriptParser::ClassNode *>(p_context._class);
- base_type.is_meta_type = p_context.function && p_context.function->_static;
- base = p_context.base;
-
- function = static_cast<const GDScriptParser::IdentifierNode *>(op->arguments[0])->name;
-
- if (function == "connect" && op->arguments.size() >= 4) {
- _guess_expression_type(p_context, op->arguments[3], connect_base);
- }
+ return;
}
GDScriptCompletionIdentifier ci;
ci.type = base_type;
ci.value = base;
- _find_call_arguments(p_context, ci, function, p_argidx, _static, r_result, r_arghint);
-
- if (function == "connect" && p_argidx == 2) {
- Map<String, ScriptCodeCompletionOption> methods;
- _find_identifiers_in_base(p_context, connect_base, true, methods);
- for (Map<String, ScriptCodeCompletionOption>::Element *E = methods.front(); E; E = E->next()) {
- ScriptCodeCompletionOption &option = E->value();
- option.insert_text = quote_style + option.display + quote_style;
- r_result.insert(option.display, option);
- }
- }
+ _find_call_arguments(p_context, ci, call->function_name, p_argidx, _static, r_result, r_arghint);
r_forced = r_result.size() > 0;
}
@@ -2516,150 +2364,213 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
GDScriptParser parser;
+ GDScriptAnalyzer analyzer(&parser);
+
+ parser.parse(p_code, p_path, true);
+ analyzer.analyze();
- parser.parse(p_code, p_path.get_base_dir(), false, p_path, true);
r_forced = false;
Map<String, ScriptCodeCompletionOption> options;
- GDScriptCompletionContext context;
- context._class = parser.get_completion_class();
- context.block = parser.get_completion_block();
- context.function = parser.get_completion_function();
- context.line = parser.get_completion_line();
-
- if (!context._class || context._class->owner == nullptr) {
- context.base = p_owner;
- context.base_path = p_path.get_base_dir();
- }
+ GDScriptParser::CompletionContext completion_context = parser.get_completion_context();
+ completion_context.base = p_owner;
bool is_function = false;
- switch (parser.get_completion_type()) {
- case GDScriptParser::COMPLETION_NONE: {
+ switch (completion_context.type) {
+ case GDScriptParser::COMPLETION_NONE:
+ break;
+ case GDScriptParser::COMPLETION_ANNOTATION: {
+ List<MethodInfo> annotations;
+ parser.get_annotation_list(&annotations);
+ for (const List<MethodInfo>::Element *E = annotations.front(); E != nullptr; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get().name.substr(1), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ if (E->get().arguments.size() > 0) {
+ option.insert_text += "(";
+ }
+ options.insert(option.display, option);
+ }
+ r_forced = true;
+ } break;
+ case GDScriptParser::COMPLETION_ANNOTATION_ARGUMENTS: {
+ if (completion_context.node == nullptr || completion_context.node->type != GDScriptParser::Node::ANNOTATION) {
+ break;
+ }
+ const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node);
+ _find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options);
+ r_forced = true;
} break;
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
List<StringName> constants;
- Variant::get_constants_for_type(parser.get_completion_built_in_constant(), &constants);
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ Variant::get_constants_for_type(completion_context.builtin_type, &constants);
+ for (const List<StringName>::Element *E = constants.front(); E != nullptr; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
options.insert(option.display, option);
}
} break;
- case GDScriptParser::COMPLETION_PARENT_FUNCTION: {
- _find_identifiers_in_class(context, !context.function || context.function->_static, true, true, options);
+ case GDScriptParser::COMPLETION_INHERIT_TYPE: {
+ _list_available_types(true, completion_context, options);
+ r_forced = true;
} break;
- case GDScriptParser::COMPLETION_FUNCTION: {
- is_function = true;
- [[fallthrough]];
+ case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: {
+ ScriptCodeCompletionOption option("void", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ options.insert(option.display, option);
}
- case GDScriptParser::COMPLETION_IDENTIFIER: {
- _find_identifiers(context, is_function, options);
+ [[fallthrough]];
+ case GDScriptParser::COMPLETION_TYPE_NAME: {
+ _list_available_types(false, completion_context, options);
+ r_forced = true;
} break;
- case GDScriptParser::COMPLETION_GET_NODE: {
- if (p_owner) {
- List<String> opts;
- p_owner->get_argument_options("get_node", 0, &opts);
-
- for (List<String>::Element *E = opts.front(); E; E = E->next()) {
- String opt = E->get().strip_edges();
- if (opt.is_quoted()) {
- r_forced = true;
- String idopt = opt.unquote();
- if (idopt.replace("/", "_").is_valid_identifier()) {
- ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- } else {
- ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- }
- }
+ case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: {
+ _list_available_types(false, completion_context, options);
+ ScriptCodeCompletionOption get("get", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ options.insert(get.display, get);
+ ScriptCodeCompletionOption set("set", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ options.insert(set.display, set);
+ r_forced = true;
+ } break;
+ case GDScriptParser::COMPLETION_PROPERTY_DECLARATION: {
+ ScriptCodeCompletionOption get("get", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ options.insert(get.display, get);
+ ScriptCodeCompletionOption set("set", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ options.insert(set.display, set);
+ r_forced = true;
+ } break;
+ case GDScriptParser::COMPLETION_PROPERTY_METHOD: {
+ if (!completion_context.current_class) {
+ break;
+ }
+ for (int i = 0; i < completion_context.current_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &member = completion_context.current_class->members[i];
+ if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {
+ continue;
}
-
- // Get autoloads
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
- }
- String name = s.get_slice("/", 1);
- ScriptCodeCompletionOption option(quote_style + "/root/" + name + quote_style, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
+ if (member.function->is_static) {
+ continue;
}
+ ScriptCodeCompletionOption option(member.function->identifier->name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ options.insert(option.display, option);
}
+ r_forced = true;
} break;
- case GDScriptParser::COMPLETION_METHOD: {
- is_function = true;
- [[fallthrough]];
- }
- case GDScriptParser::COMPLETION_INDEX: {
- const GDScriptParser::Node *node = parser.get_completion_node();
- if (node->type != GDScriptParser::Node::TYPE_OPERATOR) {
+ case GDScriptParser::COMPLETION_ASSIGN: {
+ GDScriptCompletionIdentifier type;
+ if (!completion_context.node || completion_context.node->type != GDScriptParser::Node::ASSIGNMENT) {
break;
}
- const GDScriptParser::OperatorNode *op = static_cast<const GDScriptParser::OperatorNode *>(node);
- if (op->arguments.size() < 1) {
+ if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) {
+ _find_identifiers(completion_context, false, options, 0);
+ r_forced = true;
break;
}
+ if (!type.enumeration.empty()) {
+ _find_enumeration_candidates(completion_context, type.enumeration, options);
+ r_forced = options.size() > 0;
+ } else {
+ _find_identifiers(completion_context, false, options, 0);
+ r_forced = true;
+ }
+ } break;
+ case GDScriptParser::COMPLETION_METHOD:
+ is_function = true;
+ [[fallthrough]];
+ case GDScriptParser::COMPLETION_IDENTIFIER: {
+ _find_identifiers(completion_context, is_function, options, 0);
+ } break;
+ case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:
+ is_function = true;
+ [[fallthrough]];
+ case GDScriptParser::COMPLETION_ATTRIBUTE: {
+ r_forced = true;
+ const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
+ if (attr->base) {
+ GDScriptCompletionIdentifier base;
+ if (!_guess_expression_type(completion_context, attr->base, base)) {
+ break;
+ }
+
+ _find_identifiers_in_base(base, is_function, options, 0);
+ }
+ } break;
+ case GDScriptParser::COMPLETION_SUBSCRIPT: {
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(context, op->arguments[0], base)) {
+ if (!_guess_expression_type(completion_context, subscript->base, base)) {
break;
}
- GDScriptCompletionContext c = context;
- c.function = nullptr;
- c.block = nullptr;
+ GDScriptParser::CompletionContext c = completion_context;
+ c.current_function = nullptr;
+ c.current_suite = nullptr;
c.base = base.value.get_type() == Variant::OBJECT ? base.value.operator Object *() : nullptr;
if (base.type.kind == GDScriptParser::DataType::CLASS) {
- c._class = base.type.class_type;
+ c.current_class = base.type.class_type;
} else {
- c._class = nullptr;
+ c.current_class = nullptr;
}
- _find_identifiers_in_base(c, base, is_function, options);
+ _find_identifiers_in_base(base, false, options, 0);
+ } break;
+ case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {
+ if (!completion_context.current_class) {
+ break;
+ }
+ const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node);
+ bool found = true;
+ GDScriptCompletionIdentifier base;
+ base.type.kind = GDScriptParser::DataType::CLASS;
+ base.type.type_source = GDScriptParser::DataType::INFERRED;
+ base.type.is_constant = true;
+ base.type.class_type = completion_context.current_class;
+ base.value = completion_context.base;
+
+ for (int i = 0; i < completion_context.current_argument; i++) {
+ GDScriptCompletionIdentifier ci;
+ if (!_guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci)) {
+ found = false;
+ break;
+ }
+ base = ci;
+ }
+
+ // TODO: Improve this to only list types.
+ if (found) {
+ _find_identifiers_in_base(base, false, options, 0);
+ }
+ r_forced = true;
+ } break;
+ case GDScriptParser::COMPLETION_RESOURCE_PATH: {
+ if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) {
+ _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
+ r_forced = true;
+ }
} break;
case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {
- _find_call_arguments(context, parser.get_completion_node(), parser.get_completion_argument_index(), options, r_forced, r_call_hint);
+ if (!completion_context.node) {
+ break;
+ }
+ _find_call_arguments(completion_context, completion_context.node, completion_context.current_argument, options, r_forced, r_call_hint);
} break;
- case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
- GDScriptParser::DataType native_type = context._class->base_type;
- while (native_type.has_type && native_type.kind != GDScriptParser::DataType::NATIVE) {
+ case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {
+ GDScriptParser::DataType native_type = completion_context.current_class->base_type;
+ while (native_type.is_set() && native_type.kind != GDScriptParser::DataType::NATIVE) {
switch (native_type.kind) {
case GDScriptParser::DataType::CLASS: {
native_type = native_type.class_type->base_type;
} break;
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<GDScript> gds = native_type.script_type;
- if (gds.is_valid()) {
- Ref<GDScript> base = gds->get_base_script();
- if (base.is_valid()) {
- native_type.script_type = base;
- } else {
- native_type.native_type = gds->get_instance_base_type();
- native_type.kind = GDScriptParser::DataType::NATIVE;
- }
- } else {
- native_type.has_type = false;
- }
- } break;
default: {
- native_type.has_type = false;
+ native_type.kind = GDScriptParser::DataType::UNRESOLVED;
} break;
}
}
- if (!native_type.has_type) {
+ if (!native_type.is_set()) {
break;
}
- StringName class_name = native_type.native_type;
+ StringName class_name = _get_real_class_name(native_type.native_type);
if (!ClassDB::class_exists(class_name)) {
- class_name = String("_") + class_name;
- if (!ClassDB::class_exists(class_name)) {
- break;
- }
+ break;
}
bool use_type_hint = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints").operator bool();
@@ -2711,245 +2622,41 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
options.insert(option.display, option);
}
} break;
- case GDScriptParser::COMPLETION_YIELD: {
- const GDScriptParser::Node *node = parser.get_completion_node();
-
- GDScriptCompletionContext c = context;
- c.line = node->line;
- GDScriptCompletionIdentifier type;
- if (!_guess_expression_type(c, node, type)) {
- break;
- }
+ case GDScriptParser::COMPLETION_GET_NODE: {
+ if (p_owner) {
+ List<String> opts;
+ p_owner->get_argument_options("get_node", 0, &opts);
- GDScriptParser::DataType base_type = type.type;
- while (base_type.has_type) {
- switch (base_type.kind) {
- case GDScriptParser::DataType::CLASS: {
- for (int i = 0; i < base_type.class_type->_signals.size(); i++) {
- ScriptCodeCompletionOption option(base_type.class_type->_signals[i].name.operator String(), ScriptCodeCompletionOption::KIND_SIGNAL);
- option.insert_text = quote_style + option.display + quote_style;
+ for (List<String>::Element *E = opts.front(); E; E = E->next()) {
+ String opt = E->get().strip_edges();
+ if (opt.is_quoted()) {
+ r_forced = true;
+ String idopt = opt.unquote();
+ if (idopt.replace("/", "_").is_valid_identifier()) {
+ ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH);
options.insert(option.display, option);
- }
- base_type = base_type.class_type->base_type;
- } break;
- case GDScriptParser::DataType::SCRIPT:
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<Script> scr = base_type.script_type;
- if (scr.is_valid()) {
- List<MethodInfo> signals;
- scr->get_script_signal_list(&signals);
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(quote_style + E->get().name + quote_style, ScriptCodeCompletionOption::KIND_SIGNAL);
- options.insert(option.display, option);
- }
- Ref<Script> base_script = scr->get_base_script();
- if (base_script.is_valid()) {
- base_type.script_type = base_script;
- } else {
- base_type.kind = GDScriptParser::DataType::NATIVE;
- base_type.native_type = scr->get_instance_base_type();
- }
} else {
- base_type.has_type = false;
- }
- } break;
- case GDScriptParser::DataType::NATIVE: {
- base_type.has_type = false;
-
- StringName class_name = base_type.native_type;
- if (!ClassDB::class_exists(class_name)) {
- class_name = String("_") + class_name;
- if (!ClassDB::class_exists(class_name)) {
- break;
- }
- }
-
- List<MethodInfo> signals;
- ClassDB::get_signal_list(class_name, &signals);
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(quote_style + E->get().name + quote_style, ScriptCodeCompletionOption::KIND_SIGNAL);
+ ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
options.insert(option.display, option);
}
- } break;
- default: {
- base_type.has_type = false;
}
}
- }
- } break;
- case GDScriptParser::COMPLETION_RESOURCE_PATH: {
- if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) {
- _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
- r_forced = true;
- }
- } break;
- case GDScriptParser::COMPLETION_ASSIGN: {
- GDScriptCompletionIdentifier type;
- if (!_guess_expression_type(context, parser.get_completion_node(), type)) {
- break;
- }
- if (!type.enumeration.empty()) {
- _find_enumeration_candidates(type.enumeration, options);
- r_forced = options.size() > 0;
- }
- } break;
- case GDScriptParser::COMPLETION_TYPE_HINT: {
- const GDScriptParser::ClassNode *clss = context._class;
- while (clss) {
- for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = clss->constant_expressions.front(); E; E = E->next()) {
- GDScriptCompletionIdentifier constant;
- GDScriptCompletionContext c = context;
- c.function = nullptr;
- c.block = nullptr;
- c.line = E->value().expression->line;
- if (_guess_expression_type(c, E->value().expression, constant)) {
- if (constant.type.has_type && constant.type.is_meta_type) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
- }
- }
- for (int i = 0; i < clss->subclasses.size(); i++) {
- if (clss->subclasses[i]->name != StringName()) {
- ScriptCodeCompletionOption option(clss->subclasses[i]->name.operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
- }
- clss = clss->owner;
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- ScriptCodeCompletionOption option(Variant::get_type_name((Variant::Type)i), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
- }
- ScriptCodeCompletionOption option(s.get_slice("/", 1), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
- }
+ // Get autoloads.
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- List<StringName> native_classes;
- ClassDB::get_class_list(&native_classes);
- for (List<StringName>::Element *E = native_classes.front(); E; E = E->next()) {
- String class_name = E->get().operator String();
- if (class_name.begins_with("_")) {
- class_name = class_name.right(1);
- }
- if (Engine::get_singleton()->has_singleton(class_name)) {
- continue;
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ String name = E->key();
+ ScriptCodeCompletionOption option(quote_style + "/root/" + name + quote_style, ScriptCodeCompletionOption::KIND_NODE_PATH);
+ options.insert(option.display, option);
}
- ScriptCodeCompletionOption option(class_name, ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
-
- // Named scripts
- List<StringName> named_scripts;
- ScriptServer::get_global_class_list(&named_scripts);
- for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
}
-
- if (parser.get_completion_identifier_is_function()) {
- ScriptCodeCompletionOption option("void", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- options.insert(option.display, option);
- }
- r_forced = true;
} break;
- case GDScriptParser::COMPLETION_TYPE_HINT_INDEX: {
- GDScriptCompletionIdentifier base;
- String index = parser.get_completion_cursor().operator String();
- if (!_guess_identifier_type(context, index.get_slice(".", 0), base)) {
+ case GDScriptParser::COMPLETION_SUPER_METHOD: {
+ if (!completion_context.current_class) {
break;
}
-
- GDScriptCompletionContext c = context;
- c._class = nullptr;
- c.function = nullptr;
- c.block = nullptr;
- bool finding = true;
- index = index.right(index.find(".") + 1);
- while (index.find(".") != -1) {
- String id = index.get_slice(".", 0);
-
- GDScriptCompletionIdentifier sub_base;
- if (!_guess_identifier_type_from_base(c, base, id, sub_base)) {
- finding = false;
- break;
- }
- index = index.right(index.find(".") + 1);
- base = sub_base;
- }
-
- if (!finding) {
- break;
- }
-
- GDScriptParser::DataType base_type = base.type;
- while (base_type.has_type) {
- switch (base_type.kind) {
- case GDScriptParser::DataType::CLASS: {
- if (base_type.class_type) {
- for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = base_type.class_type->constant_expressions.front(); E; E = E->next()) {
- GDScriptCompletionIdentifier constant;
- GDScriptCompletionContext c2 = context;
- c2._class = base_type.class_type;
- c2.function = nullptr;
- c2.block = nullptr;
- c2.line = E->value().expression->line;
- if (_guess_expression_type(c2, E->value().expression, constant)) {
- if (constant.type.has_type && constant.type.is_meta_type) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
- }
- }
- for (int i = 0; i < base_type.class_type->subclasses.size(); i++) {
- if (base_type.class_type->subclasses[i]->name != StringName()) {
- ScriptCodeCompletionOption option(base_type.class_type->subclasses[i]->name.operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
- }
-
- base_type = base_type.class_type->base_type;
- } else {
- base_type.has_type = false;
- }
- } break;
- case GDScriptParser::DataType::SCRIPT:
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<Script> scr = base_type.script_type;
- if (scr.is_valid()) {
- Map<StringName, Variant> constants;
- scr->get_constants(&constants);
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- Ref<Script> const_scr = E->value();
- if (const_scr.is_valid()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
- options.insert(option.display, option);
- }
- }
- Ref<Script> base_script = scr->get_base_script();
- if (base_script.is_valid()) {
- base_type.script_type = base_script;
- } else {
- base_type.has_type = false;
- }
- } else {
- base_type.has_type = false;
- }
- } break;
- default: {
- base_type.has_type = false;
- } break;
- }
- }
- r_forced = options.size() > 0;
+ _find_identifiers_in_class(completion_context.current_class, true, false, true, options, 0);
} break;
}
@@ -3056,45 +2763,19 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, const String &p_symbol, bool p_is_function, GDScriptLanguage::LookupResult &r_result) {
GDScriptParser::DataType base_type = p_base;
- while (base_type.has_type) {
+ while (base_type.is_set()) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
if (base_type.class_type) {
- if (p_is_function) {
- for (int i = 0; i < base_type.class_type->functions.size(); i++) {
- if (base_type.class_type->functions[i]->name == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = base_type.class_type->functions[i]->line;
- return OK;
- }
- }
- for (int i = 0; i < base_type.class_type->static_functions.size(); i++) {
- if (base_type.class_type->static_functions[i]->name == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = base_type.class_type->static_functions[i]->line;
- return OK;
- }
- }
- } else {
- if (base_type.class_type->constant_expressions.has(p_symbol)) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = base_type.class_type->constant_expressions[p_symbol].expression->line;
- return OK;
- }
-
- for (int i = 0; i < base_type.class_type->variables.size(); i++) {
- if (base_type.class_type->variables[i].identifier == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = base_type.class_type->variables[i].line;
- return OK;
- }
- }
+ if (base_type.class_type->has_member(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = base_type.class_type->get_member(p_symbol).get_line();
+ return OK;
}
base_type = base_type.class_type->base_type;
}
} break;
- case GDScriptParser::DataType::SCRIPT:
- case GDScriptParser::DataType::GDSCRIPT: {
+ case GDScriptParser::DataType::SCRIPT: {
Ref<Script> scr = base_type.script_type;
if (scr.is_valid()) {
int line = scr->get_member_line(p_symbol);
@@ -3112,17 +2793,14 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
base_type.native_type = scr->get_instance_base_type();
}
} else {
- base_type.has_type = false;
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = base_type.native_type;
+ StringName class_name = _get_real_class_name(base_type.native_type);
if (!ClassDB::class_exists(class_name)) {
- class_name = String("_") + class_name;
- if (!ClassDB::class_exists(class_name)) {
- base_type.has_type = false;
- break;
- }
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
+ break;
}
if (ClassDB::has_method(class_name, p_symbol, true)) {
@@ -3162,15 +2840,11 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
}
- List<PropertyInfo> properties;
- ClassDB::get_property_list(class_name, &properties, true);
- for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().name == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
- r_result.class_name = base_type.native_type;
- r_result.class_member = p_symbol;
- return OK;
- }
+ if (ClassDB::has_property(class_name, p_symbol, true)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
}
StringName parent = ClassDB::get_parent_class(class_name);
@@ -3181,11 +2855,11 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
base_type.native_type = parent;
}
} else {
- base_type.has_type = false;
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
}
} break;
case GDScriptParser::DataType::BUILTIN: {
- base_type.has_type = false;
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
if (Variant::has_constant(base_type.builtin_type, p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
@@ -3201,7 +2875,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
v = v_ref;
} else {
Callable::CallError err;
- v = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ v = Variant::construct(base_type.builtin_type, NULL, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
break;
}
@@ -3224,7 +2898,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
} break;
default: {
- base_type.has_type = false;
+ base_type.kind = GDScriptParser::DataType::UNRESOLVED;
} break;
}
}
@@ -3273,26 +2947,18 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
GDScriptParser parser;
- parser.parse(p_code, p_path.get_base_dir(), false, p_path, true);
-
- if (parser.get_completion_type() == GDScriptParser::COMPLETION_NONE) {
- return ERR_CANT_RESOLVE;
- }
+ parser.parse(p_code, p_path, true);
+ GDScriptAnalyzer analyzer(&parser);
+ analyzer.analyze();
- GDScriptCompletionContext context;
- context._class = parser.get_completion_class();
- context.function = parser.get_completion_function();
- context.block = parser.get_completion_block();
- context.line = parser.get_completion_line();
- context.base = p_owner;
- context.base_path = p_path.get_base_dir();
+ GDScriptParser::CompletionContext context = parser.get_completion_context();
- if (context._class && context._class->extends_class.size() > 0) {
+ if (context.current_class && context.current_class->extends.size() > 0) {
bool success = false;
- ClassDB::get_integer_constant(context._class->extends_class[0], p_symbol, &success);
+ ClassDB::get_integer_constant(context.current_class->extends[0], p_symbol, &success);
if (success) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = context._class->extends_class[0];
+ r_result.class_name = context.current_class->extends[0];
r_result.class_member = p_symbol;
return OK;
}
@@ -3300,57 +2966,40 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
bool is_function = false;
- switch (parser.get_completion_type()) {
+ switch (context.type) {
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
- r_result.class_name = Variant::get_type_name(parser.get_completion_built_in_constant());
+ r_result.class_name = Variant::get_type_name(context.builtin_type);
r_result.class_member = p_symbol;
return OK;
} break;
- case GDScriptParser::COMPLETION_PARENT_FUNCTION:
- case GDScriptParser::COMPLETION_FUNCTION: {
+ case GDScriptParser::COMPLETION_SUPER_METHOD:
+ case GDScriptParser::COMPLETION_METHOD: {
is_function = true;
[[fallthrough]];
}
case GDScriptParser::COMPLETION_IDENTIFIER: {
- if (!is_function) {
- is_function = parser.get_completion_identifier_is_function();
- }
-
GDScriptParser::DataType base_type;
- if (context._class) {
- if (parser.get_completion_type() != GDScriptParser::COMPLETION_PARENT_FUNCTION) {
- base_type.has_type = true;
- base_type.kind = GDScriptParser::DataType::CLASS;
- base_type.class_type = const_cast<GDScriptParser::ClassNode *>(context._class);
+ if (context.current_class) {
+ if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {
+ base_type = context.current_class->get_datatype();
} else {
- base_type = context._class->base_type;
+ base_type = context.current_class->base_type;
}
} else {
break;
}
- if (!is_function && context.block) {
- // Lookup local variables
- const GDScriptParser::BlockNode *block = context.block;
- while (block) {
- if (block->variables.has(p_symbol)) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = block->variables[p_symbol]->line;
- return OK;
- }
- block = block->parent_block;
- }
- }
-
- if (context.function && context.function->name != StringName()) {
- // Lookup function arguments
- for (int i = 0; i < context.function->arguments.size(); i++) {
- if (context.function->arguments[i] == p_symbol) {
+ if (!is_function && context.current_suite) {
+ // Lookup local variables.
+ const GDScriptParser::SuiteNode *suite = context.current_suite;
+ while (suite) {
+ if (suite->has_local(p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = context.function->line;
+ r_result.location = suite->get_local(p_symbol).start_line;
return OK;
}
+ suite = suite->parent_block;
}
}
@@ -3359,38 +3008,27 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
if (!is_function) {
- // Guess in autoloads as singletons
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
- }
- String name = s.get_slice("/", 1);
- if (name == String(p_symbol)) {
- String path = ProjectSettings::get_singleton()->get(s);
- if (path.begins_with("*")) {
- String script = path.substr(1, path.length());
-
- if (!script.ends_with(".gd")) {
- // Not a script, try find the script anyway,
- // may have some success
- script = script.get_basename() + ".gd";
- }
+ // Guess in autoloads as singletons.
+ if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {
+ const ProjectSettings::AutoloadInfo &singleton = ProjectSettings::get_singleton()->get_autoload(p_symbol);
+ if (singleton.is_singleton) {
+ String script = singleton.path;
+ if (!script.ends_with(".gd")) {
+ // Not a script, try find the script anyway,
+ // may have some success.
+ script = script.get_basename() + ".gd";
+ }
- if (FileAccess::exists(script)) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
- r_result.location = 0;
- r_result.script = ResourceLoader::load(script);
- return OK;
- }
+ if (FileAccess::exists(script)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location = 0;
+ r_result.script = ResourceLoader::load(script);
+ return OK;
}
}
}
- // Global
+ // Global.
Map<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map();
if (classes.has(p_symbol)) {
Variant value = GDScriptLanguage::get_singleton()->get_global_array()[classes[p_symbol]];
@@ -3417,7 +3055,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
// We cannot determine the exact nature of the identifier here
// Otherwise these codes would work
StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true);
- if (enumName != nullptr) {
+ if (enumName != NULL) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
r_result.class_name = "@GlobalScope";
r_result.class_member = enumName;
@@ -3437,17 +3075,20 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
} break;
- case GDScriptParser::COMPLETION_METHOD: {
+ case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD: {
is_function = true;
[[fallthrough]];
}
- case GDScriptParser::COMPLETION_INDEX: {
- const GDScriptParser::Node *node = parser.get_completion_node();
- if (node->type != GDScriptParser::Node::TYPE_OPERATOR) {
+ case GDScriptParser::COMPLETION_ATTRIBUTE: {
+ if (context.node->type != GDScriptParser::Node::SUBSCRIPT) {
+ break;
+ }
+ const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(context.node);
+ if (!subscript->is_attribute) {
break;
}
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(context, static_cast<const GDScriptParser::OperatorNode *>(node)->arguments[0], base)) {
+ if (!_guess_expression_type(context, subscript->base, base)) {
break;
}
@@ -3455,18 +3096,16 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
} break;
- case GDScriptParser::COMPLETION_VIRTUAL_FUNC: {
- GDScriptParser::DataType base_type = context._class->base_type;
+ case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {
+ GDScriptParser::DataType base_type = context.current_class->base_type;
if (_lookup_symbol_from_base(base_type, p_symbol, true, r_result) == OK) {
return OK;
}
} break;
- case GDScriptParser::COMPLETION_TYPE_HINT: {
- GDScriptParser::DataType base_type = context._class->base_type;
- base_type.has_type = true;
- base_type.kind = GDScriptParser::DataType::CLASS;
- base_type.class_type = const_cast<GDScriptParser::ClassNode *>(context._class);
+ case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID:
+ case GDScriptParser::COMPLETION_TYPE_NAME: {
+ GDScriptParser::DataType base_type = context.current_class->get_datatype();
if (_lookup_symbol_from_base(base_type, p_symbol, false, r_result) == OK) {
return OK;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 92e07ab874..e59f99fc56 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -34,6 +34,10 @@
#include "gdscript.h"
#include "gdscript_functions.h"
+#ifdef DEBUG_ENABLED
+#include "core/string_builder.h"
+#endif
+
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
int address = p_address & ADDR_MASK;
@@ -105,9 +109,9 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
#ifdef TOOLS_ENABLED
case ADDR_TYPE_NAMED_GLOBAL: {
#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _named_globals_count, nullptr);
+ ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
#endif
- StringName id = _named_globals_ptr[address];
+ StringName id = _global_names_ptr[address];
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
@@ -210,12 +214,11 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_CONSTRUCT_DICTIONARY, \
&&OPCODE_CALL, \
&&OPCODE_CALL_RETURN, \
+ &&OPCODE_CALL_ASYNC, \
&&OPCODE_CALL_BUILT_IN, \
- &&OPCODE_CALL_SELF, \
&&OPCODE_CALL_SELF_BASE, \
- &&OPCODE_YIELD, \
- &&OPCODE_YIELD_SIGNAL, \
- &&OPCODE_YIELD_RESUME, \
+ &&OPCODE_AWAIT, \
+ &&OPCODE_AWAIT_RESUME, \
&&OPCODE_JUMP, \
&&OPCODE_JUMP_IF, \
&&OPCODE_JUMP_IF_NOT, \
@@ -227,7 +230,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_BREAKPOINT, \
&&OPCODE_LINE, \
&&OPCODE_END \
- };
+ }; \
+ static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
#define OPCODE(m_op) \
m_op:
@@ -280,7 +284,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int line = _initial_line;
if (p_state) {
- //use existing (supplied) state (yielded)
+ //use existing (supplied) state (awaited)
stack = (Variant *)p_state->stack.ptr();
call_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
line = p_state->line;
@@ -363,6 +367,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
}
+ static_ref = script;
+
String err_text;
#ifdef DEBUG_ENABLED
@@ -409,7 +415,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
profile.frame_call_count++;
}
bool exit_ok = false;
- bool yielded = false;
+ bool awaited = false;
#endif
#ifdef DEBUG_ENABLED
@@ -1004,10 +1010,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_CALL_ASYNC)
OPCODE(OPCODE_CALL_RETURN)
OPCODE(OPCODE_CALL) {
CHECK_SPACE(4);
- bool call_ret = _code_ptr[ip] == OPCODE_CALL_RETURN;
+ bool call_ret = _code_ptr[ip] != OPCODE_CALL;
+#ifdef DEBUG_ENABLED
+ bool call_async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
+#endif
int argc = _code_ptr[ip + 1];
GET_VARIANT_PTR(base, 2);
@@ -1038,6 +1048,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (call_ret) {
GET_VARIANT_PTR(ret, argc);
base->call_ptr(*methodname, (const Variant **)argptrs, argc, ret, err);
+#ifdef DEBUG_ENABLED
+ if (!call_async && ret->get_type() == Variant::OBJECT) {
+ // Check if getting a function state without await.
+ bool was_freed = false;
+ Object *obj = ret->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Got a freed object as a result of the call.";
+ OPCODE_BREAK;
+ }
+ if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ err_text = R"(Trying to call an async function without "await".)";
+ OPCODE_BREAK;
+ }
+ }
+#endif
} else {
base->call_ptr(*methodname, (const Variant **)argptrs, argc, nullptr, err);
}
@@ -1116,10 +1142,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
- OPCODE(OPCODE_CALL_SELF) {
- OPCODE_BREAK;
- }
-
OPCODE(OPCODE_CALL_SELF_BASE) {
CHECK_SPACE(2);
int self_fun = _code_ptr[ip + 1];
@@ -1190,105 +1212,95 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
- OPCODE(OPCODE_YIELD)
- OPCODE(OPCODE_YIELD_SIGNAL) {
- int ipofs = 1;
- if (_code_ptr[ip] == OPCODE_YIELD_SIGNAL) {
- CHECK_SPACE(4);
- ipofs += 2;
- } else {
- CHECK_SPACE(2);
- }
+ OPCODE(OPCODE_AWAIT) {
+ CHECK_SPACE(2);
- Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
- gdfs->function = this;
+ //do the oneshot connect
+ GET_VARIANT_PTR(argobj, 1);
+
+ Signal sig;
+ bool is_signal = true;
- gdfs->state.stack.resize(alloca_size);
- //copy variant stack
- for (int i = 0; i < _stack_size; i++) {
- memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
- }
- gdfs->state.stack_size = _stack_size;
- gdfs->state.self = self;
- gdfs->state.alloca_size = alloca_size;
- gdfs->state.ip = ip + ipofs;
- gdfs->state.line = line;
- gdfs->state.script = _script;
{
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
- _script->pending_func_states.add(&gdfs->scripts_list);
- if (p_instance) {
- gdfs->state.instance = p_instance;
- p_instance->pending_func_states.add(&gdfs->instances_list);
- } else {
- gdfs->state.instance = nullptr;
- }
- }
-#ifdef DEBUG_ENABLED
- gdfs->state.function_name = name;
- gdfs->state.script_path = _script->get_path();
-#endif
- gdfs->state.defarg = defarg;
- gdfs->function = this;
+ Variant result = *argobj;
- retvalue = gdfs;
+ if (argobj->get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ Object *obj = argobj->get_validated_object_with_check(was_freed);
- if (_code_ptr[ip] == OPCODE_YIELD_SIGNAL) {
- //do the oneshot connect
- GET_VARIANT_PTR(argobj, 1);
- GET_VARIANT_PTR(argname, 2);
+ if (was_freed) {
+ err_text = "Trying to await on a freed object.";
+ OPCODE_BREAK;
+ }
-#ifdef DEBUG_ENABLED
- if (argobj->get_type() != Variant::OBJECT) {
- err_text = "First argument of yield() not of type object.";
- OPCODE_BREAK;
- }
- if (argname->get_type() != Variant::STRING) {
- err_text = "Second argument of yield() not a string (for signal name).";
- OPCODE_BREAK;
+ // Is this even possible to be null at this point?
+ if (obj) {
+ if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ static StringName completed = _scs_create("completed");
+ result = Signal(obj, completed);
+ }
+ }
}
-#endif
-
-#ifdef DEBUG_ENABLED
- bool was_freed;
- Object *obj = argobj->get_validated_object_with_check(was_freed);
- String signal = argname->operator String();
- if (was_freed) {
- err_text = "First argument of yield() is a previously freed instance.";
- OPCODE_BREAK;
+ if (result.get_type() != Variant::SIGNAL) {
+ ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
+ // The stack pointer should be the same, so we don't need to set a return value.
+ is_signal = false;
+ } else {
+ sig = result;
}
+ }
- if (!obj) {
- err_text = "First argument of yield() is null.";
- OPCODE_BREAK;
+ if (is_signal) {
+ Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
+ gdfs->function = this;
+
+ gdfs->state.stack.resize(alloca_size);
+ //copy variant stack
+ for (int i = 0; i < _stack_size; i++) {
+ memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
}
- if (signal.length() == 0) {
- err_text = "Second argument of yield() is an empty string (for signal name).";
- OPCODE_BREAK;
+ gdfs->state.stack_size = _stack_size;
+ gdfs->state.self = self;
+ gdfs->state.alloca_size = alloca_size;
+ gdfs->state.ip = ip + 2;
+ gdfs->state.line = line;
+ gdfs->state.script = _script;
+ {
+ MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ _script->pending_func_states.add(&gdfs->scripts_list);
+ if (p_instance) {
+ gdfs->state.instance = p_instance;
+ p_instance->pending_func_states.add(&gdfs->instances_list);
+ } else {
+ gdfs->state.instance = nullptr;
+ }
}
+#ifdef DEBUG_ENABLED
+ gdfs->state.function_name = name;
+ gdfs->state.script_path = _script->get_path();
+#endif
+ gdfs->state.defarg = defarg;
+ gdfs->function = this;
- Error err = obj->connect_compat(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT);
+ retvalue = gdfs;
+
+ Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
if (err != OK) {
- err_text = "Error connecting to signal: " + signal + " during yield().";
+ err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
OPCODE_BREAK;
}
-#else
- Object *obj = argobj->operator Object *();
- String signal = argname->operator String();
-
- obj->connect_compat(signal, gdfs.ptr(), "_signal_callback", varray(gdfs), Object::CONNECT_ONESHOT);
-#endif
- }
#ifdef DEBUG_ENABLED
- exit_ok = true;
- yielded = true;
+ exit_ok = true;
+ awaited = true;
#endif
- OPCODE_BREAK;
+ OPCODE_BREAK;
+ }
}
+ DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
- OPCODE(OPCODE_YIELD_RESUME) {
+ OPCODE(OPCODE_AWAIT_RESUME) {
CHECK_SPACE(2);
#ifdef DEBUG_ENABLED
if (!p_state) {
@@ -1430,11 +1442,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
GET_VARIANT_PTR(test, 1);
- GET_VARIANT_PTR(message, 2);
bool result = test->booleanize();
if (!result) {
- const String &message_str = *message;
+ String message_str;
+ if (_code_ptr[ip + 2] != 0) {
+ GET_VARIANT_PTR(message, 2);
+ message_str = *message;
+ }
if (message_str.empty()) {
err_text = "Assertion failed.";
} else {
@@ -1551,11 +1566,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
}
- // Check if this is the last time the function is resuming from yield
- // Will be true if never yielded as well
+ // Check if this is the last time the function is resuming from await
+ // Will be true if never awaited as well
// When it's the last resume it will postpone the exit from stack,
// so the debugger knows which function triggered the resume of the next function (if any)
- if (!p_state || yielded) {
+ if (!p_state || awaited) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->exit_function();
}
@@ -1781,14 +1796,14 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
if (!scripts_list.in_list()) {
#ifdef DEBUG_ENABLED
- ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after yield, but script is gone. At script: " + state.script_path + ":" + itos(state.line));
+ ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but script is gone. At script: " + state.script_path + ":" + itos(state.line));
#else
return Variant();
#endif
}
if (state.instance && !instances_list.in_list()) {
#ifdef DEBUG_ENABLED
- ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after yield, but class instance is gone. At script: " + state.script_path + ":" + itos(state.line));
+ ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but class instance is gone. At script: " + state.script_path + ":" + itos(state.line));
#else
return Variant();
#endif
@@ -1805,7 +1820,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
bool completed = true;
// If the return value is a GDScriptFunctionState reference,
- // then the function did yield again after resuming.
+ // then the function did awaited again after resuming.
if (ret.is_ref()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
@@ -1818,8 +1833,6 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
state.result = Variant();
if (completed) {
- _clear_stack();
-
if (first_state.is_valid()) {
first_state->emit_signal("completed", ret);
} else {
@@ -1869,3 +1882,506 @@ GDScriptFunctionState::~GDScriptFunctionState() {
instances_list.remove_from_list();
}
}
+
+#ifdef DEBUG_ENABLED
+static String _get_variant_string(const Variant &p_variant) {
+ String txt;
+ if (p_variant.get_type() == Variant::STRING) {
+ txt = "\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::STRING_NAME) {
+ txt = "&\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::NODE_PATH) {
+ txt = "^\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::OBJECT) {
+ Object *obj = p_variant;
+ if (!obj) {
+ txt = "null";
+ } else {
+ GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
+ if (cls) {
+ txt += cls->get_name();
+ txt += " (class)";
+ } else {
+ txt = obj->get_class();
+ if (obj->get_script_instance()) {
+ txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
+ }
+ }
+ }
+ } else {
+ txt = p_variant;
+ }
+ return txt;
+}
+
+static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
+ int addr = p_address & GDScriptFunction::ADDR_MASK;
+
+ switch (p_address >> GDScriptFunction::ADDR_BITS) {
+ case GDScriptFunction::ADDR_TYPE_SELF: {
+ return "self";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS: {
+ return "class";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_MEMBER: {
+ return "member(" + p_script->debug_get_member_by_index(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
+ return "class_const(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
+ return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK: {
+ return "stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
+ return "var_stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_GLOBAL: {
+ return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
+ return "named_global(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NIL: {
+ return "nil";
+ } break;
+ }
+
+ return "<err>";
+}
+
+void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
+#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
+
+ for (int ip = 0; ip < _code_size;) {
+ StringBuilder text;
+ int incr = 0;
+
+ text += " ";
+ text += itos(ip);
+ text += ": ";
+
+ // This makes the compiler complain if some opcode is unchecked in the switch.
+ Opcode code = Opcode(_code_ptr[ip]);
+
+ switch (code) {
+ case OPCODE_OPERATOR: {
+ int operation = _code_ptr[ip + 1];
+
+ text += "operator ";
+
+ text += DADDR(4);
+ text += " = ";
+ text += DADDR(2);
+ text += " ";
+ text += Variant::get_operator_name(Variant::Operator(operation));
+ text += " ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_EXTENDS_TEST: {
+ text += "is object ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_IS_BUILTIN: {
+ text += "is builtin ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 2]));
+
+ incr += 4;
+ } break;
+ case OPCODE_SET: {
+ text += "set ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_GET: {
+ text += "get ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_NAMED: {
+ text += "set_named ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"] = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED: {
+ text += "get_named ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_MEMBER: {
+ text += "set_member ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 1]];
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_GET_MEMBER: {
+ text += "get_member ";
+ text += DADDR(2);
+ text += " = ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 1]];
+ text += "\"]";
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN_TRUE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = true";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_FALSE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = false";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_TYPED_BUILTIN: {
+ text += "assign typed builtin (";
+ text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 1]);
+ text += ") ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_NATIVE: {
+ Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+ text += "assign typed native (";
+ text += nc->get_name().operator String();
+ text += ") ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_SCRIPT: {
+ Variant script = _constants_ptr[_code_ptr[ip + 1]];
+ Script *sc = Object::cast_to<Script>(script.operator Object *());
+
+ text += "assign typed script (";
+ text += sc->get_path();
+ text += ") ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_BUILTIN: {
+ text += "cast builtin ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(2);
+ text += " as ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_NATIVE: {
+ Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+ text += "cast native ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(2);
+ text += " as ";
+ text += nc->get_name();
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_SCRIPT: {
+ text += "cast ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(2);
+ text += " as ";
+ text += DADDR(1);
+
+ incr += 4;
+ } break;
+ case OPCODE_CONSTRUCT: {
+ Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
+ int argc = _code_ptr[ip + 2];
+
+ text += "construct ";
+ text += DADDR(3 + argc);
+ text += " = ";
+
+ text += Variant::get_type_name(t) + "(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(i + 3);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_ARRAY: {
+ int argc = _code_ptr[ip + 1];
+ text += " make_array ";
+ text += DADDR(2 + argc);
+ text += " = [";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(2 + i);
+ }
+
+ text += "]";
+
+ incr += 3 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_DICTIONARY: {
+ int argc = _code_ptr[ip + 1];
+ text += "make_dict ";
+ text += DADDR(2 + argc * 2);
+ text += " = {";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(2 + i * 2 + 0);
+ text += ": ";
+ text += DADDR(2 + i * 2 + 1);
+ }
+
+ text += "}";
+
+ incr += 3 + argc * 2;
+ } break;
+ case OPCODE_CALL:
+ case OPCODE_CALL_RETURN:
+ case OPCODE_CALL_ASYNC: {
+ bool ret = _code_ptr[ip] == OPCODE_CALL_RETURN;
+ bool async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
+
+ if (ret) {
+ text += "call-ret ";
+ } else if (async) {
+ text += "call-async ";
+ } else {
+ text += "call ";
+ }
+
+ int argc = _code_ptr[ip + 1];
+ if (ret || async) {
+ text += DADDR(4 + argc) + " = ";
+ }
+
+ text += DADDR(2) + ".";
+ text += String(_global_names_ptr[_code_ptr[ip + 3]]);
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(4 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_BUILT_IN: {
+ text += "call-built-in ";
+
+ int argc = _code_ptr[ip + 2];
+ text += DADDR(3 + argc) + " = ";
+
+ text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 1]));
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(3 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_SELF_BASE: {
+ text += "call-self-base ";
+
+ int argc = _code_ptr[ip + 2];
+ text += DADDR(3 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 1]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(3 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_AWAIT: {
+ text += "await ";
+ text += DADDR(1);
+
+ incr += 2;
+ } break;
+ case OPCODE_AWAIT_RESUME: {
+ text += "await resume ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP: {
+ text += "jump ";
+ text += itos(_code_ptr[ip + 1]);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP_IF: {
+ text += "jump-if ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_IF_NOT: {
+ text += "jump-if-not ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_TO_DEF_ARGUMENT: {
+ text += "jump-to-default-argument ";
+
+ incr = 1;
+ } break;
+ case OPCODE_RETURN: {
+ text += "return ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_ITERATE_BEGIN: {
+ text += "for-init ";
+ text += DADDR(4);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 3]);
+
+ incr += 5;
+ } break;
+ case OPCODE_ITERATE: {
+ text += "for-loop ";
+ text += DADDR(4);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 3]);
+
+ incr += 5;
+ } break;
+ case OPCODE_LINE: {
+ int line = _code_ptr[ip + 1] - 1;
+ if (line >= 0 && line < p_code_lines.size()) {
+ text += "line ";
+ text += itos(line + 1);
+ text += ": ";
+ text += p_code_lines[line];
+ } else {
+ text += "";
+ }
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSERT: {
+ text += "assert (";
+ text += DADDR(1);
+ text += ", ";
+ text += DADDR(2);
+ text += ")";
+
+ incr += 3;
+ } break;
+ case OPCODE_BREAKPOINT: {
+ text += "breakpoint";
+
+ incr += 1;
+ } break;
+ case OPCODE_END: {
+ text += "== END ==";
+
+ incr += 1;
+ } break;
+ }
+
+ ip += incr;
+ if (text.get_string_length() > 0) {
+ print_line(text.as_string());
+ }
+ }
+}
+#endif
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 583eab744a..c98ac09310 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -56,7 +56,8 @@ struct GDScriptDataType {
bool has_type = false;
Variant::Type builtin_type = Variant::NIL;
StringName native_type;
- Ref<Script> script_type;
+ Script *script_type = nullptr;
+ Ref<Script> script_type_ref;
bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
if (!has_type) {
@@ -180,12 +181,11 @@ public:
OPCODE_CONSTRUCT_DICTIONARY,
OPCODE_CALL,
OPCODE_CALL_RETURN,
+ OPCODE_CALL_ASYNC,
OPCODE_CALL_BUILT_IN,
- OPCODE_CALL_SELF,
OPCODE_CALL_SELF_BASE,
- OPCODE_YIELD,
- OPCODE_YIELD_SIGNAL,
- OPCODE_YIELD_RESUME,
+ OPCODE_AWAIT,
+ OPCODE_AWAIT_RESUME,
OPCODE_JUMP,
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
@@ -224,6 +224,7 @@ public:
private:
friend class GDScriptCompiler;
+ friend class GDScriptByteCodeGenerator;
StringName source;
@@ -232,10 +233,6 @@ private:
int _constant_count;
const StringName *_global_names_ptr;
int _global_names_count;
-#ifdef TOOLS_ENABLED
- const StringName *_named_globals_ptr;
- int _named_globals_count;
-#endif
const int *_default_arg_ptr;
int _default_arg_count;
const int *_code_ptr;
@@ -252,9 +249,6 @@ private:
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
-#ifdef TOOLS_ENABLED
- Vector<StringName> named_globals;
-#endif
Vector<int> default_arguments;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
@@ -344,6 +338,10 @@ public:
Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state = nullptr);
+#ifdef DEBUG_ENABLED
+ void disassemble(const Vector<String> &p_code_lines) const;
+#endif
+
_FORCE_INLINE_ MultiplayerAPI::RPCMode get_rpc_mode() const { return rpc_mode; }
GDScriptFunction();
~GDScriptFunction();
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index d4258c385e..31ce63bc6e 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -146,12 +146,14 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
if (p_arg_count < m_count) { \
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
r_error.argument = m_count; \
+ r_error.expected = m_count; \
r_ret = Variant(); \
return; \
} \
if (p_arg_count > m_count) { \
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
r_error.argument = m_count; \
+ r_error.expected = m_count; \
r_ret = Variant(); \
return; \
}
@@ -633,7 +635,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
case TEXT_CHAR: {
VALIDATE_ARG_COUNT(1);
VALIDATE_ARG_NUM(0);
- CharType result[2] = { *p_args[0], 0 };
+ char32_t result[2] = { *p_args[0], 0 };
r_ret = String(result);
} break;
case TEXT_ORD: {
@@ -897,6 +899,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
case 0: {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 1;
+ r_error.expected = 1;
r_ret = Variant();
} break;
@@ -1001,6 +1004,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
default: {
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument = 3;
+ r_error.expected = 3;
r_ret = Variant();
} break;
@@ -1632,7 +1636,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case MATH_SMOOTHSTEP: {
- MethodInfo mi("smoothstep", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
+ MethodInfo mi("smoothstep", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "s"));
mi.return_val.type = Variant::FLOAT;
return mi;
} break;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 57531ed13b..2a69db130b 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -30,8767 +30,3830 @@
#include "gdscript_parser.h"
-#include "core/core_string_names.h"
-#include "core/engine.h"
#include "core/io/resource_loader.h"
+#include "core/math/math_defs.h"
#include "core/os/file_access.h"
-#include "core/print_string.h"
#include "core/project_settings.h"
-#include "core/reference.h"
-#include "core/script_language.h"
#include "gdscript.h"
-template <class T>
-T *GDScriptParser::alloc_node() {
- T *t = memnew(T);
-
- t->next = list;
- list = t;
+#ifdef DEBUG_ENABLED
+#include "core/os/os.h"
+#include "core/string_builder.h"
+#endif // DEBUG_ENABLED
- if (!head) {
- head = t;
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif // TOOLS_ENABLED
+
+static HashMap<StringName, Variant::Type> builtin_types;
+Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
+ if (builtin_types.empty()) {
+ builtin_types["bool"] = Variant::BOOL;
+ builtin_types["int"] = Variant::INT;
+ builtin_types["float"] = Variant::FLOAT;
+ builtin_types["String"] = Variant::STRING;
+ builtin_types["Vector2"] = Variant::VECTOR2;
+ builtin_types["Vector2i"] = Variant::VECTOR2I;
+ builtin_types["Rect2"] = Variant::RECT2;
+ builtin_types["Rect2i"] = Variant::RECT2I;
+ builtin_types["Transform2D"] = Variant::TRANSFORM2D;
+ builtin_types["Vector3"] = Variant::VECTOR3;
+ builtin_types["Vector3i"] = Variant::VECTOR3I;
+ builtin_types["AABB"] = Variant::AABB;
+ builtin_types["Plane"] = Variant::PLANE;
+ builtin_types["Quat"] = Variant::QUAT;
+ builtin_types["Basis"] = Variant::BASIS;
+ builtin_types["Transform"] = Variant::TRANSFORM;
+ builtin_types["Color"] = Variant::COLOR;
+ builtin_types["RID"] = Variant::_RID;
+ builtin_types["Object"] = Variant::OBJECT;
+ builtin_types["StringName"] = Variant::STRING_NAME;
+ builtin_types["NodePath"] = Variant::NODE_PATH;
+ builtin_types["Dictionary"] = Variant::DICTIONARY;
+ builtin_types["Callable"] = Variant::CALLABLE;
+ builtin_types["Signal"] = Variant::SIGNAL;
+ builtin_types["Array"] = Variant::ARRAY;
+ builtin_types["PackedByteArray"] = Variant::PACKED_BYTE_ARRAY;
+ builtin_types["PackedInt32Array"] = Variant::PACKED_INT32_ARRAY;
+ builtin_types["PackedInt64Array"] = Variant::PACKED_INT64_ARRAY;
+ builtin_types["PackedFloat32Array"] = Variant::PACKED_FLOAT32_ARRAY;
+ builtin_types["PackedFloat64Array"] = Variant::PACKED_FLOAT64_ARRAY;
+ builtin_types["PackedStringArray"] = Variant::PACKED_STRING_ARRAY;
+ builtin_types["PackedVector2Array"] = Variant::PACKED_VECTOR2_ARRAY;
+ builtin_types["PackedVector3Array"] = Variant::PACKED_VECTOR3_ARRAY;
+ builtin_types["PackedColorArray"] = Variant::PACKED_COLOR_ARRAY;
+ // NIL is not here, hence the -1.
+ if (builtin_types.size() != Variant::VARIANT_MAX - 1) {
+ ERR_PRINT("Outdated parser: amount of built-in types don't match the amount of types in Variant.");
+ }
+ }
+
+ if (builtin_types.has(p_type)) {
+ return builtin_types[p_type];
+ }
+ return Variant::VARIANT_MAX;
+}
+
+void GDScriptParser::cleanup() {
+ builtin_types.clear();
+}
+
+GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) {
+ for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
+ if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) {
+ return GDScriptFunctions::Function(i);
+ }
+ }
+ return GDScriptFunctions::FUNC_MAX;
+}
+
+void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
+ List<StringName> keys;
+ valid_annotations.get_key_list(&keys);
+ for (const List<StringName>::Element *E = keys.front(); E != nullptr; E = E->next()) {
+ r_annotations->push_back(valid_annotations[E->get()].info);
}
+}
- t->line = tokenizer->get_token_line();
- t->column = tokenizer->get_token_column();
- return t;
+GDScriptParser::GDScriptParser() {
+ // Register valid annotations.
+ // TODO: Should this be static?
+ // TODO: Validate applicable types (e.g. a VARIABLE annotation that only applies to string variables).
+ register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
+ register_annotation(MethodInfo("@icon", { Variant::STRING, "icon_path" }), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
+ register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
+ // Export annotations.
+ register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_TYPE_STRING, Variant::NIL>);
+ register_annotation(MethodInfo("@export_enum", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, 0, true);
+ register_annotation(MethodInfo("@export_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, 1, true);
+ register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
+ register_annotation(MethodInfo("@export_global_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, 1, true);
+ register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
+ register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
+ register_annotation(MethodInfo("@export_placeholder"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
+ register_annotation(MethodInfo("@export_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, 3);
+ register_annotation(MethodInfo("@export_exp_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_RANGE, Variant::FLOAT>, 3);
+ register_annotation(MethodInfo("@export_exp_easing", { Variant::STRING, "hint1" }, { Variant::STRING, "hint2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, 2);
+ register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
+ register_annotation(MethodInfo("@export_node_path", { Variant::STRING, "type" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, 1, true);
+ register_annotation(MethodInfo("@export_flags", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, 0, true);
+ register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
+ // Networking.
+ register_annotation(MethodInfo("@remote"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_REMOTE>);
+ register_annotation(MethodInfo("@master"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_MASTER>);
+ register_annotation(MethodInfo("@puppet"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>);
+ register_annotation(MethodInfo("@remotesync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_REMOTESYNC>);
+ register_annotation(MethodInfo("@mastersync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_MASTERSYNC>);
+ register_annotation(MethodInfo("@puppetsync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPETSYNC>);
+ // TODO: Warning annotations.
}
-#ifdef DEBUG_ENABLED
-static String _find_function_name(const GDScriptParser::OperatorNode *p_call);
-#endif // DEBUG_ENABLED
+GDScriptParser::~GDScriptParser() {
+ clear();
+}
-bool GDScriptParser::_end_statement() {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) {
- tokenizer->advance();
- return true; //handle next
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE || tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
- return true; //will be handled properly
+void GDScriptParser::clear() {
+ while (list != nullptr) {
+ Node *element = list;
+ list = list->next;
+ memdelete(element);
}
- return false;
+ head = nullptr;
+ list = nullptr;
+ _is_tool = false;
+ for_completion = false;
+ errors.clear();
+ multiline_stack.clear();
}
-void GDScriptParser::_set_end_statement_error(String p_name) {
- String error_msg;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER) {
- error_msg = vformat("Expected end of statement (\"%s\"), got %s (\"%s\") instead.", p_name, tokenizer->get_token_name(tokenizer->get_token()), tokenizer->get_token_identifier());
+void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
+ // TODO: Improve error reporting by pointing at source code.
+ // TODO: Errors might point at more than one place at once (e.g. show previous declaration).
+ panic_mode = true;
+ // TODO: Improve positional information.
+ if (p_origin == nullptr) {
+ errors.push_back({ p_message, current.start_line, current.start_column });
} else {
- error_msg = vformat("Expected end of statement (\"%s\"), got %s instead.", p_name, tokenizer->get_token_name(tokenizer->get_token()));
+ errors.push_back({ p_message, p_origin->start_line, p_origin->leftmost_column });
}
- _set_error(error_msg);
}
-bool GDScriptParser::_enter_indent_block(BlockNode *p_block) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COLON) {
- // report location at the previous token (on the previous line)
- int error_line = tokenizer->get_token_line(-1);
- int error_column = tokenizer->get_token_column(-1);
- _set_error("':' expected at end of line.", error_line, error_column);
- return false;
+#ifdef DEBUG_ENABLED
+void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
+ Vector<String> symbols;
+ if (!p_symbol1.empty()) {
+ symbols.push_back(p_symbol1);
}
- tokenizer->advance();
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
- return false;
+ if (!p_symbol2.empty()) {
+ symbols.push_back(p_symbol2);
}
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) {
- // be more python-like
- IndentLevel current_level = indent_level.back()->get();
- indent_level.push_back(current_level);
- return true;
- //_set_error("newline expected after ':'.");
- //return false;
- }
-
- while (true) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) {
- return false; //wtf
- } else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_EOF) {
- return false;
- } else if (tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) {
- int indent = tokenizer->get_token_line_indent();
- int tabs = tokenizer->get_token_line_tab_indent();
- IndentLevel current_level = indent_level.back()->get();
- IndentLevel new_indent(indent, tabs);
- if (new_indent.is_mixed(current_level)) {
- _set_error("Mixed tabs and spaces in indentation.");
- return false;
- }
-
- if (indent <= current_level.indent) {
- return false;
- }
-
- indent_level.push_back(new_indent);
- tokenizer->advance();
- return true;
-
- } else if (p_block) {
- NewLineNode *nl = alloc_node<NewLineNode>();
- nl->line = tokenizer->get_token_line();
- p_block->statements.push_back(nl);
- }
-
- tokenizer->advance(); // go to next newline
+ if (!p_symbol3.empty()) {
+ symbols.push_back(p_symbol3);
}
+ if (!p_symbol4.empty()) {
+ symbols.push_back(p_symbol4);
+ }
+ push_warning(p_source, p_code, symbols);
}
-bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete, bool p_parsing_constant) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- tokenizer->advance();
- } else {
- parenthesis++;
- int argidx = 0;
-
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- _make_completable_call(argidx);
- completion_node = p_parent;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING && tokenizer->get_token(1) == GDScriptTokenizer::TK_CURSOR) {
- //completing a string argument..
- completion_cursor = tokenizer->get_token_constant();
-
- _make_completable_call(argidx);
- completion_node = p_parent;
- tokenizer->advance(1);
- return false;
- }
-
- Node *arg = _parse_expression(p_parent, p_static, false, p_parsing_constant);
- if (!arg) {
- return false;
- }
-
- p_args.push_back(arg);
+void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) {
+ if (is_ignoring_warnings) {
+ return;
+ }
+ if (GLOBAL_GET("debug/gdscript/warnings/exclude_addons").booleanize() && script_path.begins_with("res://addons/")) {
+ return;
+ }
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- tokenizer->advance();
- break;
+ String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
+ if (ignored_warnings.has(warn_name)) {
+ return;
+ }
+ if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) {
+ return;
+ }
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- if (tokenizer->get_token(1) == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expression expected");
- return false;
- }
+ GDScriptWarning warning;
+ warning.code = p_code;
+ warning.symbols = p_symbols;
+ warning.start_line = p_source->start_line;
+ warning.end_line = p_source->end_line;
+ warning.leftmost_column = p_source->leftmost_column;
+ warning.rightmost_column = p_source->rightmost_column;
- tokenizer->advance();
- argidx++;
- } else {
- // something is broken
- _set_error("Expected ',' or ')'");
- return false;
- }
+ List<GDScriptWarning>::Element *before = nullptr;
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E != nullptr; E = E->next()) {
+ if (E->get().start_line > warning.start_line) {
+ break;
}
- parenthesis--;
+ before = E;
+ }
+ if (before) {
+ warnings.insert_after(before, warning);
+ } else {
+ warnings.push_front(warning);
}
-
- return true;
}
+#endif
-void GDScriptParser::_make_completable_call(int p_arg) {
- completion_cursor = StringName();
- completion_type = COMPLETION_CALL_ARGUMENTS;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_argument = p_arg;
- completion_block = current_block;
- completion_found = true;
- tokenizer->advance();
-}
-
-bool GDScriptParser::_get_completable_identifier(CompletionType p_type, StringName &identifier) {
- identifier = StringName();
- if (tokenizer->is_token_literal()) {
- identifier = tokenizer->get_token_literal();
- tokenizer->advance();
- }
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- completion_cursor = identifier;
- completion_type = p_type;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_block = current_block;
- completion_found = true;
- completion_ident_is_call = false;
- tokenizer->advance();
-
- if (tokenizer->is_token_literal()) {
- identifier = identifier.operator String() + tokenizer->get_token_literal().operator String();
- tokenizer->advance();
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- completion_ident_is_call = true;
- }
- return true;
+void GDScriptParser::make_completion_context(CompletionType p_type, Node *p_node, int p_argument, bool p_force) {
+ if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
+ return;
}
-
- return false;
+ if (previous.cursor_place != GDScriptTokenizer::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizer::CURSOR_END && current.cursor_place == GDScriptTokenizer::CURSOR_NONE) {
+ return;
+ }
+ CompletionContext context;
+ context.type = p_type;
+ context.current_class = current_class;
+ context.current_function = current_function;
+ context.current_suite = current_suite;
+ context.current_line = tokenizer.get_cursor_line();
+ context.current_argument = p_argument;
+ context.node = p_node;
+ completion_context = context;
}
-GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_static, bool p_allow_assign, bool p_parsing_constant) {
- //Vector<Node*> expressions;
- //Vector<OperatorNode::Operator> operators;
-
- Vector<Expression> expression;
-
- Node *expr = nullptr;
-
- int op_line = tokenizer->get_token_line(); // when operators are created at the bottom, the line might have been changed (\n found)
-
- while (true) {
- /*****************/
- /* Parse Operand */
- /*****************/
-
- if (parenthesis > 0) {
- //remove empty space (only allowed if inside parenthesis
- while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance();
- }
- }
-
- // Check that the next token is not TK_CURSOR and if it is, the offset should be incremented.
- int next_valid_offset = 1;
- if (tokenizer->get_token(next_valid_offset) == GDScriptTokenizer::TK_CURSOR) {
- next_valid_offset++;
- // There is a chunk of the identifier that also needs to be ignored (not always there!)
- if (tokenizer->get_token(next_valid_offset) == GDScriptTokenizer::TK_IDENTIFIER) {
- next_valid_offset++;
- }
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- //subexpression ()
- tokenizer->advance();
- parenthesis++;
- Node *subexpr = _parse_expression(p_parent, p_static, p_allow_assign, p_parsing_constant);
- parenthesis--;
- if (!subexpr) {
- return nullptr;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')' in expression");
- return nullptr;
- }
-
- tokenizer->advance();
- expr = subexpr;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_DOLLAR) {
- tokenizer->advance();
-
- String path;
-
- bool need_identifier = true;
- bool done = false;
- int line = tokenizer->get_token_line();
-
- while (!done) {
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_CURSOR: {
- completion_type = COMPLETION_GET_NODE;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_cursor = path;
- completion_argument = 0;
- completion_block = current_block;
- completion_found = true;
- tokenizer->advance();
- } break;
- case GDScriptTokenizer::TK_CONSTANT: {
- if (!need_identifier) {
- done = true;
- break;
- }
-
- if (tokenizer->get_token_constant().get_type() != Variant::STRING) {
- _set_error("Expected string constant or identifier after '$' or '/'.");
- return nullptr;
- }
-
- path += String(tokenizer->get_token_constant());
- tokenizer->advance();
- need_identifier = false;
-
- } break;
- case GDScriptTokenizer::TK_OP_DIV: {
- if (need_identifier) {
- done = true;
- break;
- }
-
- path += "/";
- tokenizer->advance();
- need_identifier = true;
-
- } break;
- default: {
- // Instead of checking for TK_IDENTIFIER, we check with is_token_literal, as this allows us to use match/sync/etc. as a name
- if (need_identifier && tokenizer->is_token_literal()) {
- path += String(tokenizer->get_token_literal());
- tokenizer->advance();
- need_identifier = false;
- } else {
- done = true;
- }
-
- break;
- }
- }
- }
-
- if (path == "") {
- _set_error("Path expected after $.");
- return nullptr;
- }
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_CALL;
- op->line = line;
- op->arguments.push_back(alloc_node<SelfNode>());
- op->arguments[0]->line = line;
-
- IdentifierNode *funcname = alloc_node<IdentifierNode>();
- funcname->name = "get_node";
- funcname->line = line;
- op->arguments.push_back(funcname);
-
- ConstantNode *nodepath = alloc_node<ConstantNode>();
- nodepath->value = NodePath(StringName(path));
- nodepath->datatype = _type_from_variant(nodepath->value);
- nodepath->line = line;
- op->arguments.push_back(nodepath);
-
- expr = op;
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- tokenizer->advance();
- continue; //no point in cursor in the middle of expression
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT) {
- //constant defined by tokenizer
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = tokenizer->get_token_constant();
- constant->datatype = _type_from_variant(constant->value);
- tokenizer->advance();
- expr = constant;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_PI) {
- //constant defined by tokenizer
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = Math_PI;
- constant->datatype = _type_from_variant(constant->value);
- tokenizer->advance();
- expr = constant;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_TAU) {
- //constant defined by tokenizer
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = Math_TAU;
- constant->datatype = _type_from_variant(constant->value);
- tokenizer->advance();
- expr = constant;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_INF) {
- //constant defined by tokenizer
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = Math_INF;
- constant->datatype = _type_from_variant(constant->value);
- tokenizer->advance();
- expr = constant;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONST_NAN) {
- //constant defined by tokenizer
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = Math_NAN;
- constant->datatype = _type_from_variant(constant->value);
- tokenizer->advance();
- expr = constant;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_PRELOAD) {
- //constant defined by tokenizer
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after 'preload'");
- return nullptr;
- }
- tokenizer->advance();
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- completion_cursor = StringName();
- completion_node = p_parent;
- completion_type = COMPLETION_RESOURCE_PATH;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_block = current_block;
- completion_argument = 0;
- completion_found = true;
- tokenizer->advance();
- }
+void GDScriptParser::make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force) {
+ if (!for_completion || (!p_force && completion_context.type != COMPLETION_NONE)) {
+ return;
+ }
+ if (previous.cursor_place != GDScriptTokenizer::CURSOR_MIDDLE && previous.cursor_place != GDScriptTokenizer::CURSOR_END && current.cursor_place == GDScriptTokenizer::CURSOR_NONE) {
+ return;
+ }
+ CompletionContext context;
+ context.type = p_type;
+ context.current_class = current_class;
+ context.current_function = current_function;
+ context.current_suite = current_suite;
+ context.current_line = tokenizer.get_cursor_line();
+ context.builtin_type = p_builtin_type;
+ completion_context = context;
+}
- String path;
- bool found_constant = false;
- bool valid = false;
- ConstantNode *cn;
+void GDScriptParser::push_completion_call(Node *p_call) {
+ if (!for_completion) {
+ return;
+ }
+ CompletionCall call;
+ call.call = p_call;
+ call.argument = 0;
+ completion_call_stack.push_back(call);
+ if (previous.cursor_place == GDScriptTokenizer::CURSOR_MIDDLE || previous.cursor_place == GDScriptTokenizer::CURSOR_END || current.cursor_place == GDScriptTokenizer::CURSOR_BEGINNING) {
+ completion_call = call;
+ }
+}
- Node *subexpr = _parse_and_reduce_expression(p_parent, p_static);
- if (subexpr) {
- if (subexpr->type == Node::TYPE_CONSTANT) {
- cn = static_cast<ConstantNode *>(subexpr);
- found_constant = true;
- }
- if (subexpr->type == Node::TYPE_IDENTIFIER) {
- IdentifierNode *in = static_cast<IdentifierNode *>(subexpr);
-
- // Try to find the constant expression by the identifier
- if (current_class->constant_expressions.has(in->name)) {
- Node *cn_exp = current_class->constant_expressions[in->name].expression;
- if (cn_exp->type == Node::TYPE_CONSTANT) {
- cn = static_cast<ConstantNode *>(cn_exp);
- found_constant = true;
- }
- }
- }
+void GDScriptParser::pop_completion_call() {
+ if (!for_completion) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to pop empty completion call stack");
+ completion_call_stack.pop_back();
+}
- if (found_constant && cn->value.get_type() == Variant::STRING) {
- valid = true;
- path = (String)cn->value;
- }
- }
+void GDScriptParser::set_last_completion_call_arg(int p_argument) {
+ if (!for_completion || passed_cursor) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to set argument on empty completion call stack");
+ completion_call_stack.back()->get().argument = p_argument;
+}
- if (!valid) {
- _set_error("expected string constant as 'preload' argument.");
- return nullptr;
- }
+Error GDScriptParser::parse(const String &p_source_code, const String &p_script_path, bool p_for_completion) {
+ clear();
- if (!path.is_abs_path() && base_path != "") {
- path = base_path.plus_file(path);
- }
- path = path.replace("///", "//").simplify_path();
- if (path == self_path) {
- _set_error("Can't preload itself (use 'get_script()').");
- return nullptr;
- }
+ String source = p_source_code;
+ int cursor_line = -1;
+ int cursor_column = -1;
+ for_completion = p_for_completion;
- Ref<Resource> res;
- dependencies.push_back(path);
- if (!dependencies_only) {
- if (!validating) {
- //this can be too slow for just validating code
- if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) {
- res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
- } else if (!for_completion || FileAccess::exists(path)) {
- res = ResourceLoader::load(path);
- }
- } else {
- if (!FileAccess::exists(path)) {
- _set_error("Can't preload resource at path: " + path);
- return nullptr;
- } else if (ScriptCodeCompletionCache::get_singleton()) {
- res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
- }
- }
- if (!res.is_valid()) {
- _set_error("Can't preload resource at path: " + path);
- return nullptr;
+ int tab_size = 4;
+#ifdef TOOLS_ENABLED
+ if (EditorSettings::get_singleton()) {
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ }
+#endif // TOOLS_ENABLED
+
+ if (p_for_completion) {
+ // Remove cursor sentinel char.
+ const Vector<String> lines = p_source_code.split("\n");
+ cursor_line = 1;
+ cursor_column = 1;
+ for (int i = 0; i < lines.size(); i++) {
+ bool found = false;
+ const String &line = lines[i];
+ for (int j = 0; j < line.size(); j++) {
+ if (line[j] == char32_t(0xFFFF)) {
+ found = true;
+ break;
+ } else if (line[j] == '\t') {
+ cursor_column += tab_size - 1;
}
+ cursor_column++;
}
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')' after 'preload' path");
- return nullptr;
- }
-
- Ref<GDScript> gds = res;
- if (gds.is_valid() && !gds->is_valid()) {
- _set_error("Couldn't fully preload the script, possible cyclic reference or compilation error. Use \"load()\" instead if a cyclic reference is intended.");
- return nullptr;
- }
-
- tokenizer->advance();
-
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = res;
- constant->datatype = _type_from_variant(constant->value);
-
- expr = constant;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_YIELD) {
- if (!current_function) {
- _set_error("\"yield()\" can only be used inside function blocks.");
- return nullptr;
- }
-
- current_function->has_yield = true;
-
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- _set_error("Expected \"(\" after \"yield\".");
- return nullptr;
- }
-
- tokenizer->advance();
-
- OperatorNode *yield = alloc_node<OperatorNode>();
- yield->op = OperatorNode::OP_YIELD;
-
- while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance();
+ if (found) {
+ break;
}
+ cursor_line++;
+ cursor_column = 1;
+ }
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- expr = yield;
- tokenizer->advance();
- } else {
- parenthesis++;
+ source = source.replace_first(String::chr(0xFFFF), String());
+ }
- Node *object = _parse_and_reduce_expression(p_parent, p_static);
- if (!object) {
- return nullptr;
- }
- yield->arguments.push_back(object);
+ tokenizer.set_source_code(source);
+ tokenizer.set_cursor_position(cursor_line, cursor_column);
+ script_path = p_script_path;
+ current = tokenizer.scan();
+ // Avoid error as the first token.
+ while (current.type == GDScriptTokenizer::Token::ERROR) {
+ push_error(current.literal);
+ current = tokenizer.scan();
+ }
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- _set_error("Expected \",\" after the first argument of \"yield\".");
- return nullptr;
- }
+ push_multiline(false); // Keep one for the whole parsing.
+ parse_program();
+ pop_multiline();
- tokenizer->advance();
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- completion_cursor = StringName();
- completion_node = object;
- completion_type = COMPLETION_YIELD;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_argument = 0;
- completion_block = current_block;
- completion_found = true;
- tokenizer->advance();
- }
+#ifdef DEBUG_ENABLED
+ if (multiline_stack.size() > 0) {
+ ERR_PRINT("Parser bug: Imbalanced multiline stack.");
+ }
+#endif
- Node *signal = _parse_and_reduce_expression(p_parent, p_static);
- if (!signal) {
- return nullptr;
- }
- yield->arguments.push_back(signal);
+ if (errors.empty()) {
+ return OK;
+ } else {
+ return ERR_PARSE_ERROR;
+ }
+}
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" after the second argument of \"yield\".");
- return nullptr;
- }
+GDScriptTokenizer::Token GDScriptParser::advance() {
+ if (current.type == GDScriptTokenizer::Token::TK_EOF) {
+ ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream.");
+ }
+ if (for_completion && !completion_call_stack.empty()) {
+ if (completion_call.call == nullptr && tokenizer.is_past_cursor()) {
+ completion_call = completion_call_stack.back()->get();
+ passed_cursor = true;
+ }
+ }
+ previous = current;
+ current = tokenizer.scan();
+ while (current.type == GDScriptTokenizer::Token::ERROR) {
+ push_error(current.literal);
+ current = tokenizer.scan();
+ }
+ return previous;
+}
- parenthesis--;
+bool GDScriptParser::match(GDScriptTokenizer::Token::Type p_token_type) {
+ if (!check(p_token_type)) {
+ return false;
+ }
+ advance();
+ return true;
+}
- tokenizer->advance();
+bool GDScriptParser::check(GDScriptTokenizer::Token::Type p_token_type) {
+ if (p_token_type == GDScriptTokenizer::Token::IDENTIFIER) {
+ return current.is_identifier();
+ }
+ return current.type == p_token_type;
+}
- expr = yield;
- }
+bool GDScriptParser::consume(GDScriptTokenizer::Token::Type p_token_type, const String &p_error_message) {
+ if (match(p_token_type)) {
+ return true;
+ }
+ push_error(p_error_message);
+ return false;
+}
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_SELF) {
- if (p_static) {
- _set_error("\"self\" isn't allowed in a static function or constant expression.");
- return nullptr;
- }
- //constant defined by tokenizer
- SelfNode *self = alloc_node<SelfNode>();
- tokenizer->advance();
- expr = self;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && tokenizer->get_token(1) == GDScriptTokenizer::TK_PERIOD) {
- Variant::Type bi_type = tokenizer->get_token_type();
- tokenizer->advance(2);
-
- StringName identifier;
-
- if (_get_completable_identifier(COMPLETION_BUILT_IN_TYPE_CONSTANT, identifier)) {
- completion_built_in_constant = bi_type;
- }
+bool GDScriptParser::is_at_end() {
+ return check(GDScriptTokenizer::Token::TK_EOF);
+}
- if (identifier == StringName()) {
- _set_error("Built-in type constant or static function expected after \".\".");
- return nullptr;
- }
- if (!Variant::has_constant(bi_type, identifier)) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN &&
- Variant::is_method_const(bi_type, identifier) &&
- Variant::get_method_return_type(bi_type, identifier) == bi_type) {
- tokenizer->advance();
+void GDScriptParser::synchronize() {
+ panic_mode = false;
+ while (!is_at_end()) {
+ if (previous.type == GDScriptTokenizer::Token::NEWLINE || previous.type == GDScriptTokenizer::Token::SEMICOLON) {
+ return;
+ }
- OperatorNode *construct = alloc_node<OperatorNode>();
- construct->op = OperatorNode::OP_CALL;
+ switch (current.type) {
+ case GDScriptTokenizer::Token::CLASS:
+ case GDScriptTokenizer::Token::FUNC:
+ case GDScriptTokenizer::Token::STATIC:
+ case GDScriptTokenizer::Token::VAR:
+ case GDScriptTokenizer::Token::CONST:
+ case GDScriptTokenizer::Token::SIGNAL:
+ //case GDScriptTokenizer::Token::IF: // Can also be inside expressions.
+ case GDScriptTokenizer::Token::FOR:
+ case GDScriptTokenizer::Token::WHILE:
+ case GDScriptTokenizer::Token::MATCH:
+ case GDScriptTokenizer::Token::RETURN:
+ case GDScriptTokenizer::Token::ANNOTATION:
+ return;
+ default:
+ // Do nothing.
+ break;
+ }
- TypeNode *tn = alloc_node<TypeNode>();
- tn->vtype = bi_type;
- construct->arguments.push_back(tn);
+ advance();
+ }
+}
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_CALL;
- op->arguments.push_back(construct);
+void GDScriptParser::push_multiline(bool p_state) {
+ multiline_stack.push_back(p_state);
+ tokenizer.set_multiline_mode(p_state);
+ if (p_state) {
+ // Consume potential whitespace tokens already waiting in line.
+ while (current.type == GDScriptTokenizer::Token::NEWLINE || current.type == GDScriptTokenizer::Token::INDENT || current.type == GDScriptTokenizer::Token::DEDENT) {
+ current = tokenizer.scan(); // Don't call advance() here, as we don't want to change the previous token.
+ }
+ }
+}
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = identifier;
- op->arguments.push_back(id);
+void GDScriptParser::pop_multiline() {
+ ERR_FAIL_COND_MSG(multiline_stack.size() == 0, "Parser bug: trying to pop from multiline stack without available value.");
+ multiline_stack.pop_back();
+ tokenizer.set_multiline_mode(multiline_stack.size() > 0 ? multiline_stack.back()->get() : false);
+}
- if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) {
- return nullptr;
- }
+bool GDScriptParser::is_statement_end() {
+ return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON) || check(GDScriptTokenizer::Token::TK_EOF);
+}
- expr = op;
- } else {
- // Object is a special case
- bool valid = false;
- if (bi_type == Variant::OBJECT) {
- int object_constant = ClassDB::get_integer_constant("Object", identifier, &valid);
- if (valid) {
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = object_constant;
- cn->datatype = _type_from_variant(cn->value);
- expr = cn;
- }
- }
- if (!valid) {
- _set_error("Static constant '" + identifier.operator String() + "' not present in built-in type " + Variant::get_type_name(bi_type) + ".");
- return nullptr;
- }
- }
- } else {
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = Variant::get_constant_value(bi_type, identifier);
- cn->datatype = _type_from_variant(cn->value);
- expr = cn;
- }
+void GDScriptParser::end_statement(const String &p_context) {
+ bool found = false;
+ while (is_statement_end() && !is_at_end()) {
+ // Remove sequential newlines/semicolons.
+ found = true;
+ advance();
+ }
+ if (!found && !is_at_end()) {
+ push_error(vformat(R"(Expected end of statement after %s, found "%s" instead.)", p_context, current.get_name()));
+ }
+}
- } else if (tokenizer->get_token(next_valid_offset) == GDScriptTokenizer::TK_PARENTHESIS_OPEN && tokenizer->is_token_literal()) {
- // We check with is_token_literal, as this allows us to use match/sync/etc. as a name
- //function or constructor
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_CALL;
-
- //Do a quick Array and Dictionary Check. Replace if either require no arguments.
- bool replaced = false;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
- Variant::Type ct = tokenizer->get_token_type();
- if (!p_parsing_constant) {
- if (ct == Variant::ARRAY) {
- if (tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- ArrayNode *arr = alloc_node<ArrayNode>();
- expr = arr;
- replaced = true;
- tokenizer->advance(3);
- }
- }
- if (ct == Variant::DICTIONARY) {
- if (tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- DictionaryNode *dict = alloc_node<DictionaryNode>();
- expr = dict;
- replaced = true;
- tokenizer->advance(3);
- }
- }
- }
+void GDScriptParser::parse_program() {
+ head = alloc_node<ClassNode>();
+ current_class = head;
- if (!replaced) {
- TypeNode *tn = alloc_node<TypeNode>();
- tn->vtype = tokenizer->get_token_type();
- op->arguments.push_back(tn);
- tokenizer->advance(2);
+ if (match(GDScriptTokenizer::Token::ANNOTATION)) {
+ // Check for @tool annotation.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
+ if (annotation != nullptr) {
+ if (annotation->name == "@tool") {
+ // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
+ _is_tool = true;
+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
+ push_error(R"(Expected newline after "@tool" annotation.)");
}
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_FUNC) {
- BuiltInFunctionNode *bn = alloc_node<BuiltInFunctionNode>();
- bn->function = tokenizer->get_token_built_in_func();
- op->arguments.push_back(bn);
- tokenizer->advance(2);
+ // @tool annotation has no specific target.
+ annotation->apply(this, nullptr);
} else {
- SelfNode *self = alloc_node<SelfNode>();
- op->arguments.push_back(self);
-
- StringName identifier;
- if (_get_completable_identifier(COMPLETION_FUNCTION, identifier)) {
- }
-
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = identifier;
- op->arguments.push_back(id);
- tokenizer->advance(1);
+ annotation_stack.push_back(annotation);
}
+ }
+ }
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- _make_completable_call(0);
- completion_node = op;
-
- if (op->arguments[0]->type == GDScriptParser::Node::Type::TYPE_BUILT_IN_FUNCTION) {
- BuiltInFunctionNode *bn = static_cast<BuiltInFunctionNode *>(op->arguments[0]);
- if (bn->function == GDScriptFunctions::Function::RESOURCE_LOAD) {
- completion_type = COMPLETION_RESOURCE_PATH;
- }
- }
- }
- if (!replaced) {
- if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) {
- return nullptr;
- }
- expr = op;
- }
- } else if (tokenizer->is_token_literal(0, true)) {
- // We check with is_token_literal, as this allows us to use match/sync/etc. as a name
- //identifier (reference)
-
- const ClassNode *cln = current_class;
- bool bfn = false;
- StringName identifier;
- int id_line = tokenizer->get_token_line();
- if (_get_completable_identifier(COMPLETION_IDENTIFIER, identifier)) {
- }
-
- BlockNode *b = current_block;
- while (!bfn && b) {
- if (b->variables.has(identifier)) {
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = identifier;
- id->declared_block = b;
- id->line = id_line;
- expr = id;
- bfn = true;
-
-#ifdef DEBUG_ENABLED
- LocalVarNode *lv = b->variables[identifier];
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
- case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
- case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
- case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
- case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
- case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
- case GDScriptTokenizer::TK_OP_ASSIGN_SUB: {
- if (lv->assignments == 0) {
- if (!lv->datatype.has_type) {
- _set_error("Using assignment with operation on a variable that was never assigned.");
- return nullptr;
- }
- _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, -1, identifier.operator String());
- }
- [[fallthrough]];
- }
- case GDScriptTokenizer::TK_OP_ASSIGN: {
- lv->assignments += 1;
- lv->usages--; // Assignment is not really usage
- } break;
- default: {
- lv->usages++;
- }
- }
-#endif // DEBUG_ENABLED
- break;
- }
- b = b->parent_block;
- }
-
- if (!bfn && p_parsing_constant) {
- if (cln->constant_expressions.has(identifier)) {
- expr = cln->constant_expressions[identifier].expression;
- bfn = true;
- } else if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
- //check from constants
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = GDScriptLanguage::get_singleton()->get_global_array()[GDScriptLanguage::get_singleton()->get_global_map()[identifier]];
- constant->datatype = _type_from_variant(constant->value);
- constant->line = id_line;
- expr = constant;
- bfn = true;
- }
-
- if (!bfn && GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
- //check from singletons
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = GDScriptLanguage::get_singleton()->get_named_globals_map()[identifier];
- constant->datatype = _type_from_variant(constant->value);
- expr = constant;
- bfn = true;
- }
-
- if (!dependencies_only) {
- if (!bfn && ScriptServer::is_global_class(identifier)) {
- Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
- if (scr.is_valid() && scr->is_valid()) {
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = scr;
- constant->datatype = _type_from_variant(constant->value);
- expr = constant;
- bfn = true;
- }
- }
-
- // Check parents for the constant
- if (!bfn) {
- // Using current_class instead of cln here, since cln is const*
- _determine_inheritance(current_class, false);
- if (cln->base_type.has_type && cln->base_type.kind == DataType::GDSCRIPT && cln->base_type.script_type->is_valid()) {
- Map<StringName, Variant> parent_constants;
- current_class->base_type.script_type->get_constants(&parent_constants);
- if (parent_constants.has(identifier)) {
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = parent_constants[identifier];
- constant->datatype = _type_from_variant(constant->value);
- expr = constant;
- bfn = true;
- }
- }
- }
- }
- }
-
- if (!bfn) {
-#ifdef DEBUG_ENABLED
- if (current_function) {
- int arg_idx = current_function->arguments.find(identifier);
- if (arg_idx != -1) {
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
- case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
- case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
- case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
- case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
- case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
- case GDScriptTokenizer::TK_OP_ASSIGN_SUB:
- case GDScriptTokenizer::TK_OP_ASSIGN: {
- // Assignment is not really usage
- } break;
- default: {
- current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1;
- }
- }
- }
- }
-#endif // DEBUG_ENABLED
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = identifier;
- id->line = id_line;
- expr = id;
- }
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ADD || tokenizer->get_token() == GDScriptTokenizer::TK_OP_SUB || tokenizer->get_token() == GDScriptTokenizer::TK_OP_NOT || tokenizer->get_token() == GDScriptTokenizer::TK_OP_BIT_INVERT) {
- //single prefix operators like !expr +expr -expr ++expr --expr
- alloc_node<OperatorNode>();
- Expression e;
- e.is_op = true;
-
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_OP_ADD:
- e.op = OperatorNode::OP_POS;
- break;
- case GDScriptTokenizer::TK_OP_SUB:
- e.op = OperatorNode::OP_NEG;
- break;
- case GDScriptTokenizer::TK_OP_NOT:
- e.op = OperatorNode::OP_NOT;
- break;
- case GDScriptTokenizer::TK_OP_BIT_INVERT:
- e.op = OperatorNode::OP_BIT_INVERT;
- break;
- default: {
+ for (bool should_break = false; !should_break;) {
+ // Order here doesn't matter, but there should be only one of each at most.
+ switch (current.type) {
+ case GDScriptTokenizer::Token::CLASS_NAME:
+ if (!annotation_stack.empty()) {
+ push_error(R"("class_name" should be used before annotations.)");
}
- }
-
- tokenizer->advance();
-
- if (e.op != OperatorNode::OP_NOT && tokenizer->get_token() == GDScriptTokenizer::TK_OP_NOT) {
- _set_error("Misplaced 'not'.");
- return nullptr;
- }
-
- expression.push_back(e);
- continue; //only exception, must continue...
-
- /*
- Node *subexpr=_parse_expression(op,p_static);
- if (!subexpr)
- return nullptr;
- op->arguments.push_back(subexpr);
- expr=op;*/
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_IS && tokenizer->get_token(1) == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
- // 'is' operator with built-in type
- if (!expr) {
- _set_error("Expected identifier before 'is' operator");
- return nullptr;
- }
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_IS_BUILTIN;
- op->arguments.push_back(expr);
-
- tokenizer->advance();
-
- TypeNode *tn = alloc_node<TypeNode>();
- tn->vtype = tokenizer->get_token_type();
- op->arguments.push_back(tn);
- tokenizer->advance();
-
- expr = op;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_OPEN) {
- // array
- tokenizer->advance();
-
- ArrayNode *arr = alloc_node<ArrayNode>();
- bool expecting_comma = false;
-
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
- _set_error("Unterminated array");
- return nullptr;
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_CLOSE) {
- tokenizer->advance();
- break;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance(); //ignore newline
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- if (!expecting_comma) {
- _set_error("expression or ']' expected");
- return nullptr;
- }
-
- expecting_comma = false;
- tokenizer->advance(); //ignore newline
+ advance();
+ if (head->identifier != nullptr) {
+ push_error(R"("class_name" can only be used once.)");
} else {
- //parse expression
- if (expecting_comma) {
- _set_error("',' or ']' expected");
- return nullptr;
- }
- Node *n = _parse_expression(arr, p_static, p_allow_assign, p_parsing_constant);
- if (!n) {
- return nullptr;
- }
- arr->elements.push_back(n);
- expecting_comma = true;
+ parse_class_name();
}
- }
-
- expr = arr;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) {
- // array
- tokenizer->advance();
-
- DictionaryNode *dict = alloc_node<DictionaryNode>();
-
- enum DictExpect {
-
- DICT_EXPECT_KEY,
- DICT_EXPECT_COLON,
- DICT_EXPECT_VALUE,
- DICT_EXPECT_COMMA
-
- };
-
- Node *key = nullptr;
- Set<Variant> keys;
-
- DictExpect expecting = DICT_EXPECT_KEY;
-
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
- _set_error("Unterminated dictionary");
- return nullptr;
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CURLY_BRACKET_CLOSE) {
- if (expecting == DICT_EXPECT_COLON) {
- _set_error("':' expected");
- return nullptr;
- }
- if (expecting == DICT_EXPECT_VALUE) {
- _set_error("value expected");
- return nullptr;
- }
- tokenizer->advance();
- break;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance(); //ignore newline
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- if (expecting == DICT_EXPECT_KEY) {
- _set_error("key or '}' expected");
- return nullptr;
- }
- if (expecting == DICT_EXPECT_VALUE) {
- _set_error("value expected");
- return nullptr;
- }
- if (expecting == DICT_EXPECT_COLON) {
- _set_error("':' expected");
- return nullptr;
- }
-
- expecting = DICT_EXPECT_KEY;
- tokenizer->advance(); //ignore newline
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
- if (expecting == DICT_EXPECT_KEY) {
- _set_error("key or '}' expected");
- return nullptr;
- }
- if (expecting == DICT_EXPECT_VALUE) {
- _set_error("value expected");
- return nullptr;
- }
- if (expecting == DICT_EXPECT_COMMA) {
- _set_error("',' or '}' expected");
- return nullptr;
- }
-
- expecting = DICT_EXPECT_VALUE;
- tokenizer->advance(); //ignore newline
+ break;
+ case GDScriptTokenizer::Token::EXTENDS:
+ if (!annotation_stack.empty()) {
+ push_error(R"("extends" should be used before annotations.)");
+ }
+ advance();
+ if (head->extends_used) {
+ push_error(R"("extends" can only be used once.)");
} else {
- if (expecting == DICT_EXPECT_COMMA) {
- _set_error("',' or '}' expected");
- return nullptr;
- }
- if (expecting == DICT_EXPECT_COLON) {
- _set_error("':' expected");
- return nullptr;
- }
-
- if (expecting == DICT_EXPECT_KEY) {
- if (tokenizer->is_token_literal() && tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
- // We check with is_token_literal, as this allows us to use match/sync/etc. as a name
- //lua style identifier, easier to write
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = tokenizer->get_token_literal();
- cn->datatype = _type_from_variant(cn->value);
- key = cn;
- tokenizer->advance(2);
- expecting = DICT_EXPECT_VALUE;
- } else {
- //python/js style more flexible
- key = _parse_expression(dict, p_static, p_allow_assign, p_parsing_constant);
- if (!key) {
- return nullptr;
- }
- expecting = DICT_EXPECT_COLON;
- }
- }
-
- if (expecting == DICT_EXPECT_VALUE) {
- Node *value = _parse_expression(dict, p_static, p_allow_assign, p_parsing_constant);
- if (!value) {
- return nullptr;
- }
- expecting = DICT_EXPECT_COMMA;
-
- if (key->type == GDScriptParser::Node::TYPE_CONSTANT) {
- Variant const &keyName = static_cast<const GDScriptParser::ConstantNode *>(key)->value;
-
- if (keys.has(keyName)) {
- _set_error("Duplicate key found in Dictionary literal");
- return nullptr;
- }
- keys.insert(keyName);
- }
-
- DictionaryNode::Pair pair;
- pair.key = key;
- pair.value = value;
- dict->elements.push_back(pair);
- key = nullptr;
- }
+ parse_extends();
+ end_statement("superclass");
}
- }
-
- expr = dict;
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD && (tokenizer->is_token_literal(1) || tokenizer->get_token(1) == GDScriptTokenizer::TK_CURSOR)) {
- // We check with is_token_literal, as this allows us to use match/sync/etc. as a name
- // parent call
-
- tokenizer->advance(); //goto identifier
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_PARENT_CALL;
-
- /*SelfNode *self = alloc_node<SelfNode>();
- op->arguments.push_back(self);
- forbidden for now */
- StringName identifier;
- bool is_completion = _get_completable_identifier(COMPLETION_PARENT_FUNCTION, identifier) && for_completion;
+ break;
+ default:
+ should_break = true;
+ break;
+ }
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = identifier;
- op->arguments.push_back(id);
+ if (panic_mode) {
+ synchronize();
+ }
+ }
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- if (!is_completion) {
- _set_error("Expected '(' for parent function call.");
- return nullptr;
+ if (match(GDScriptTokenizer::Token::ANNOTATION)) {
+ // Check for @icon annotation.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
+ if (annotation != nullptr) {
+ if (annotation->name == "@icon") {
+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
+ push_error(R"(Expected newline after "@icon" annotation.)");
}
+ annotation->apply(this, head);
} else {
- tokenizer->advance();
- if (!_parse_arguments(op, op->arguments, p_static, false, p_parsing_constant)) {
- return nullptr;
- }
+ annotation_stack.push_back(annotation);
}
-
- expr = op;
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && expression.size() > 0 && expression[expression.size() - 1].is_op && expression[expression.size() - 1].op == OperatorNode::OP_IS) {
- Expression e = expression[expression.size() - 1];
- e.op = OperatorNode::OP_IS_BUILTIN;
- expression.write[expression.size() - 1] = e;
-
- TypeNode *tn = alloc_node<TypeNode>();
- tn->vtype = tokenizer->get_token_type();
- expr = tn;
- tokenizer->advance();
- } else {
- //find list [ or find dictionary {
- _set_error("Error parsing expression, misplaced: " + String(tokenizer->get_token_name(tokenizer->get_token())));
- return nullptr; //nothing
}
+ }
- ERR_FAIL_COND_V_MSG(!expr, nullptr, "GDScriptParser bug, couldn't figure out what expression is.");
-
- /******************/
- /* Parse Indexing */
- /******************/
-
- while (true) {
- //expressions can be indexed any number of times
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD) {
- //indexing using "."
-
- if (tokenizer->get_token(1) != GDScriptTokenizer::TK_CURSOR && !tokenizer->is_token_literal(1)) {
- // We check with is_token_literal, as this allows us to use match/sync/etc. as a name
- _set_error("Expected identifier as member");
- return nullptr;
- } else if (tokenizer->get_token(2) == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- //call!!
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_CALL;
-
- tokenizer->advance();
-
- IdentifierNode *id = alloc_node<IdentifierNode>();
- StringName identifier;
- if (_get_completable_identifier(COMPLETION_METHOD, identifier)) {
- completion_node = op;
- //indexing stuff
- }
-
- id->name = identifier;
+ parse_class_body();
- op->arguments.push_back(expr); // call what
- op->arguments.push_back(id); // call func
- //get arguments
- tokenizer->advance(1);
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- _make_completable_call(0);
- completion_node = op;
- }
- if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) {
- return nullptr;
- }
- expr = op;
+ if (!check(GDScriptTokenizer::Token::TK_EOF)) {
+ push_error("Expected end of file.");
+ }
- } else {
- //simple indexing!
+ clear_unused_annotations();
+}
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_INDEX_NAMED;
- tokenizer->advance();
+GDScriptParser::ClassNode *GDScriptParser::parse_class() {
+ ClassNode *n_class = alloc_node<ClassNode>();
- StringName identifier;
- if (_get_completable_identifier(COMPLETION_INDEX, identifier)) {
- if (identifier == StringName()) {
- identifier = "@temp"; //so it parses alright
- }
- completion_node = op;
+ ClassNode *previous_class = current_class;
+ current_class = n_class;
+ n_class->outer = previous_class;
- //indexing stuff
- }
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the class name after "class".)")) {
+ n_class->identifier = parse_identifier();
+ }
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = identifier;
+ if (match(GDScriptTokenizer::Token::EXTENDS)) {
+ parse_extends();
+ }
- op->arguments.push_back(expr);
- op->arguments.push_back(id);
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after class declaration.)");
+ consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected newline after class declaration.)");
- expr = op;
- }
+ if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) {
+ current_class = previous_class;
+ return n_class;
+ }
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_OPEN) {
- //indexing using "[]"
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_INDEX;
+ if (match(GDScriptTokenizer::Token::EXTENDS)) {
+ if (n_class->extends_used) {
+ push_error(R"(Cannot use "extends" more than once in the same class.)");
+ }
+ parse_extends();
+ end_statement("superclass");
+ }
- tokenizer->advance(1);
+ parse_class_body();
- Node *subexpr = _parse_expression(op, p_static, p_allow_assign, p_parsing_constant);
- if (!subexpr) {
- return nullptr;
- }
+ consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
- if (tokenizer->get_token() != GDScriptTokenizer::TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return nullptr;
- }
+ current_class = previous_class;
+ return n_class;
+}
- op->arguments.push_back(expr);
- op->arguments.push_back(subexpr);
- tokenizer->advance(1);
- expr = op;
+void GDScriptParser::parse_class_name() {
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the global class name after "class_name".)")) {
+ current_class->identifier = parse_identifier();
+ }
- } else {
- break;
+ // TODO: Move this to annotation
+ if (match(GDScriptTokenizer::Token::COMMA)) {
+ // Icon path.
+ if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected class icon path string after ",".)")) {
+ if (previous.literal.get_type() != Variant::STRING) {
+ push_error(vformat(R"(Only strings can be used for the class icon path, found "%s" instead.)", Variant::get_type_name(previous.literal.get_type())));
}
+ current_class->icon_path = previous.literal;
}
+ }
- /*****************/
- /* Parse Casting */
- /*****************/
+ if (match(GDScriptTokenizer::Token::EXTENDS)) {
+ // Allow extends on the same line.
+ parse_extends();
+ end_statement("superclass");
+ } else {
+ end_statement("class_name statement");
+ }
+}
- bool has_casting = expr->type == Node::TYPE_CAST;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_AS) {
- if (has_casting) {
- _set_error("Unexpected 'as'.");
- return nullptr;
- }
- CastNode *cn = alloc_node<CastNode>();
- if (!_parse_type(cn->cast_type)) {
- _set_error("Expected type after 'as'.");
- return nullptr;
- }
- has_casting = true;
- cn->source_node = expr;
- expr = cn;
- }
+void GDScriptParser::parse_extends() {
+ current_class->extends_used = true;
- /******************/
- /* Parse Operator */
- /******************/
+ int chain_index = 0;
- if (parenthesis > 0) {
- //remove empty space (only allowed if inside parenthesis
- while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance();
- }
+ if (match(GDScriptTokenizer::Token::LITERAL)) {
+ if (previous.literal.get_type() != Variant::STRING) {
+ push_error(vformat(R"(Only strings or identifiers can be used after "extends", found "%s" instead.)", Variant::get_type_name(previous.literal.get_type())));
}
+ current_class->extends_path = previous.literal;
- Expression e;
- e.is_op = false;
- e.node = expr;
- expression.push_back(e);
+ if (!match(GDScriptTokenizer::Token::PERIOD)) {
+ return;
+ }
+ }
- // determine which operator is next
+ make_completion_context(COMPLETION_INHERIT_TYPE, current_class, chain_index++);
- OperatorNode::Operator op;
- bool valid = true;
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected superclass name after "extends".)")) {
+ return;
+ }
+ current_class->extends.push_back(previous.literal);
-//assign, if allowed is only allowed on the first operator
-#define _VALIDATE_ASSIGN \
- if (!p_allow_assign || has_casting) { \
- _set_error("Unexpected assign."); \
- return nullptr; \
- } \
- p_allow_assign = false;
+ while (match(GDScriptTokenizer::Token::PERIOD)) {
+ make_completion_context(COMPLETION_INHERIT_TYPE, current_class, chain_index++);
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected superclass name after ".".)")) {
+ return;
+ }
+ current_class->extends.push_back(previous.literal);
+ }
+}
- switch (tokenizer->get_token()) { //see operator
+template <class T>
+void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind) {
+ advance();
+ T *member = (this->*p_parse_function)();
+ if (member == nullptr) {
+ return;
+ }
+ // Consume annotations.
+ while (!annotation_stack.empty()) {
+ AnnotationNode *last_annotation = annotation_stack.back()->get();
+ if (last_annotation->applies_to(p_target)) {
+ last_annotation->apply(this, member);
+ member->annotations.push_front(last_annotation);
+ annotation_stack.pop_back();
+ } else {
+ push_error(vformat(R"(Annotation "%s" cannot be applied to a %s.)", last_annotation->name, p_member_kind));
+ clear_unused_annotations();
+ return;
+ }
+ }
+ if (member->identifier != nullptr) {
+ // Enums may be unnamed.
+ // TODO: Consider names in outer scope too, for constants and classes (and static functions?)
+ if (current_class->members_indices.has(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
+ } else {
+ current_class->add_member(member);
+ }
+ }
+}
- case GDScriptTokenizer::TK_OP_IN:
- op = OperatorNode::OP_IN;
+void GDScriptParser::parse_class_body() {
+ bool class_end = false;
+ while (!class_end && !is_at_end()) {
+ switch (current.type) {
+ case GDScriptTokenizer::Token::VAR:
+ parse_class_member(&GDScriptParser::parse_variable, AnnotationInfo::VARIABLE, "variable");
break;
- case GDScriptTokenizer::TK_OP_EQUAL:
- op = OperatorNode::OP_EQUAL;
+ case GDScriptTokenizer::Token::CONST:
+ parse_class_member(&GDScriptParser::parse_constant, AnnotationInfo::CONSTANT, "constant");
break;
- case GDScriptTokenizer::TK_OP_NOT_EQUAL:
- op = OperatorNode::OP_NOT_EQUAL;
+ case GDScriptTokenizer::Token::SIGNAL:
+ parse_class_member(&GDScriptParser::parse_signal, AnnotationInfo::SIGNAL, "signal");
break;
- case GDScriptTokenizer::TK_OP_LESS:
- op = OperatorNode::OP_LESS;
+ case GDScriptTokenizer::Token::STATIC:
+ case GDScriptTokenizer::Token::FUNC:
+ parse_class_member(&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, "function");
break;
- case GDScriptTokenizer::TK_OP_LESS_EQUAL:
- op = OperatorNode::OP_LESS_EQUAL;
+ case GDScriptTokenizer::Token::CLASS:
+ parse_class_member(&GDScriptParser::parse_class, AnnotationInfo::CLASS, "class");
break;
- case GDScriptTokenizer::TK_OP_GREATER:
- op = OperatorNode::OP_GREATER;
+ case GDScriptTokenizer::Token::ENUM:
+ parse_class_member(&GDScriptParser::parse_enum, AnnotationInfo::NONE, "enum");
break;
- case GDScriptTokenizer::TK_OP_GREATER_EQUAL:
- op = OperatorNode::OP_GREATER_EQUAL;
- break;
- case GDScriptTokenizer::TK_OP_AND:
- op = OperatorNode::OP_AND;
- break;
- case GDScriptTokenizer::TK_OP_OR:
- op = OperatorNode::OP_OR;
- break;
- case GDScriptTokenizer::TK_OP_ADD:
- op = OperatorNode::OP_ADD;
- break;
- case GDScriptTokenizer::TK_OP_SUB:
- op = OperatorNode::OP_SUB;
- break;
- case GDScriptTokenizer::TK_OP_MUL:
- op = OperatorNode::OP_MUL;
- break;
- case GDScriptTokenizer::TK_OP_DIV:
- op = OperatorNode::OP_DIV;
- break;
- case GDScriptTokenizer::TK_OP_MOD:
- op = OperatorNode::OP_MOD;
- break;
- //case GDScriptTokenizer::TK_OP_NEG: op=OperatorNode::OP_NEG ; break;
- case GDScriptTokenizer::TK_OP_SHIFT_LEFT:
- op = OperatorNode::OP_SHIFT_LEFT;
- break;
- case GDScriptTokenizer::TK_OP_SHIFT_RIGHT:
- op = OperatorNode::OP_SHIFT_RIGHT;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN: {
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN;
-
- if (tokenizer->get_token(1) == GDScriptTokenizer::TK_CURSOR) {
- //code complete assignment
- completion_type = COMPLETION_ASSIGN;
- completion_node = expr;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_block = current_block;
- completion_found = true;
- tokenizer->advance();
+ case GDScriptTokenizer::Token::ANNOTATION: {
+ advance();
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::CLASS_LEVEL);
+ if (annotation != nullptr) {
+ annotation_stack.push_back(annotation);
}
-
- } break;
- case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_ADD;
break;
- case GDScriptTokenizer::TK_OP_ASSIGN_SUB:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_SUB;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_MUL;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_DIV;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_MOD;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_SHIFT_LEFT;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_SHIFT_RIGHT;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_BIT_AND;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_BIT_OR;
- break;
- case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
- _VALIDATE_ASSIGN op = OperatorNode::OP_ASSIGN_BIT_XOR;
- break;
- case GDScriptTokenizer::TK_OP_BIT_AND:
- op = OperatorNode::OP_BIT_AND;
- break;
- case GDScriptTokenizer::TK_OP_BIT_OR:
- op = OperatorNode::OP_BIT_OR;
- break;
- case GDScriptTokenizer::TK_OP_BIT_XOR:
- op = OperatorNode::OP_BIT_XOR;
- break;
- case GDScriptTokenizer::TK_PR_IS:
- op = OperatorNode::OP_IS;
- break;
- case GDScriptTokenizer::TK_CF_IF:
- op = OperatorNode::OP_TERNARY_IF;
+ }
+ case GDScriptTokenizer::Token::PASS:
+ advance();
+ end_statement(R"("pass")");
break;
- case GDScriptTokenizer::TK_CF_ELSE:
- op = OperatorNode::OP_TERNARY_ELSE;
+ case GDScriptTokenizer::Token::DEDENT:
+ class_end = true;
break;
default:
- valid = false;
+ push_error(vformat(R"(Unexpected "%s" in class body.)", current.get_name()));
+ advance();
break;
}
-
- if (valid) {
- e.is_op = true;
- e.op = op;
- expression.push_back(e);
- tokenizer->advance();
- } else {
- break;
+ if (panic_mode) {
+ synchronize();
}
}
+}
- /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
-
- while (expression.size() > 1) {
- int next_op = -1;
- int min_priority = 0xFFFFF;
- bool is_unary = false;
- bool is_ternary = false;
-
- for (int i = 0; i < expression.size(); i++) {
- if (!expression[i].is_op) {
- continue;
- }
-
- int priority;
-
- bool unary = false;
- bool ternary = false;
- bool error = false;
- bool right_to_left = false;
-
- switch (expression[i].op) {
- case OperatorNode::OP_IS:
- case OperatorNode::OP_IS_BUILTIN:
- priority = -1;
- break; //before anything
-
- case OperatorNode::OP_BIT_INVERT:
- priority = 0;
- unary = true;
- break;
- case OperatorNode::OP_NEG:
- case OperatorNode::OP_POS:
- priority = 1;
- unary = true;
- break;
-
- case OperatorNode::OP_MUL:
- priority = 2;
- break;
- case OperatorNode::OP_DIV:
- priority = 2;
- break;
- case OperatorNode::OP_MOD:
- priority = 2;
- break;
-
- case OperatorNode::OP_ADD:
- priority = 3;
- break;
- case OperatorNode::OP_SUB:
- priority = 3;
- break;
-
- case OperatorNode::OP_SHIFT_LEFT:
- priority = 4;
- break;
- case OperatorNode::OP_SHIFT_RIGHT:
- priority = 4;
- break;
-
- case OperatorNode::OP_BIT_AND:
- priority = 5;
- break;
- case OperatorNode::OP_BIT_XOR:
- priority = 6;
- break;
- case OperatorNode::OP_BIT_OR:
- priority = 7;
- break;
-
- case OperatorNode::OP_LESS:
- priority = 8;
- break;
- case OperatorNode::OP_LESS_EQUAL:
- priority = 8;
- break;
- case OperatorNode::OP_GREATER:
- priority = 8;
- break;
- case OperatorNode::OP_GREATER_EQUAL:
- priority = 8;
- break;
-
- case OperatorNode::OP_EQUAL:
- priority = 8;
- break;
- case OperatorNode::OP_NOT_EQUAL:
- priority = 8;
- break;
+GDScriptParser::VariableNode *GDScriptParser::parse_variable() {
+ return parse_variable(true);
+}
- case OperatorNode::OP_IN:
- priority = 10;
- break;
+GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_property) {
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) {
+ return nullptr;
+ }
- case OperatorNode::OP_NOT:
- priority = 11;
- unary = true;
- break;
- case OperatorNode::OP_AND:
- priority = 12;
- break;
- case OperatorNode::OP_OR:
- priority = 13;
- break;
+ VariableNode *variable = alloc_node<VariableNode>();
+ variable->identifier = parse_identifier();
- case OperatorNode::OP_TERNARY_IF:
- priority = 14;
- ternary = true;
- right_to_left = true;
- break;
- case OperatorNode::OP_TERNARY_ELSE:
- priority = 14;
- error = true;
- // Rigth-to-left should be false in this case, otherwise it would always error.
- break;
+ if (match(GDScriptTokenizer::Token::COLON)) {
+ if (check(GDScriptTokenizer::Token::NEWLINE)) {
+ if (p_allow_property) {
+ advance();
- case OperatorNode::OP_ASSIGN:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_ADD:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_SUB:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_MUL:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_DIV:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_MOD:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_BIT_AND:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_BIT_OR:
- priority = 15;
- break;
- case OperatorNode::OP_ASSIGN_BIT_XOR:
- priority = 15;
- break;
-
- default: {
- _set_error("GDScriptParser bug, invalid operator in expression: " + itos(expression[i].op));
- return nullptr;
- }
+ return parse_property(variable, true);
+ } else {
+ push_error(R"(Expected type after ":")");
+ return nullptr;
}
-
- if (priority < min_priority || (right_to_left && priority == min_priority)) {
- // < is used for left to right (default)
- // <= is used for right to left
- if (error) {
- _set_error("Unexpected operator");
- return nullptr;
+ } else if (check((GDScriptTokenizer::Token::EQUAL))) {
+ // Infer type.
+ variable->infer_datatype = true;
+ } else {
+ if (p_allow_property) {
+ make_completion_context(COMPLETION_PROPERTY_DECLARATION_OR_TYPE, variable);
+ if (check(GDScriptTokenizer::Token::IDENTIFIER)) {
+ // Check if get or set.
+ if (current.get_identifier() == "get" || current.get_identifier() == "set") {
+ return parse_property(variable, false);
+ }
}
- next_op = i;
- min_priority = priority;
- is_unary = unary;
- is_ternary = ternary;
}
- }
- if (next_op == -1) {
- _set_error("Yet another parser bug....");
- ERR_FAIL_V(nullptr);
+ // Parse type.
+ variable->datatype_specifier = parse_type();
}
+ }
- // OK! create operator..
- if (is_unary) {
- int expr_pos = next_op;
- while (expression[expr_pos].is_op) {
- expr_pos++;
- if (expr_pos == expression.size()) {
- //can happen..
- _set_error("Unexpected end of expression...");
- return nullptr;
- }
- }
-
- //consecutively do unary operators
- for (int i = expr_pos - 1; i >= next_op; i--) {
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = expression[i].op;
- op->arguments.push_back(expression[i + 1].node);
- op->line = op_line; //line might have been changed from a \n
- expression.write[i].is_op = false;
- expression.write[i].node = op;
- expression.remove(i + 1);
- }
-
- } else if (is_ternary) {
- if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug...");
- ERR_FAIL_V(nullptr);
- }
-
- if (next_op >= (expression.size() - 2) || expression[next_op + 2].op != OperatorNode::OP_TERNARY_ELSE) {
- _set_error("Expected else after ternary if.");
- return nullptr;
- }
- if (next_op >= (expression.size() - 3)) {
- _set_error("Expected value after ternary else.");
- return nullptr;
- }
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = expression[next_op].op;
- op->line = op_line; //line might have been changed from a \n
-
- if (expression[next_op - 1].is_op) {
- _set_error("Parser bug...");
- ERR_FAIL_V(nullptr);
- }
-
- if (expression[next_op + 1].is_op) {
- // this is not invalid and can really appear
- // but it becomes invalid anyway because no binary op
- // can be followed by a unary op in a valid combination,
- // due to how precedence works, unaries will always disappear first
-
- _set_error("Unexpected two consecutive operators after ternary if.");
- return nullptr;
- }
-
- if (expression[next_op + 3].is_op) {
- // this is not invalid and can really appear
- // but it becomes invalid anyway because no binary op
- // can be followed by a unary op in a valid combination,
- // due to how precedence works, unaries will always disappear first
-
- _set_error("Unexpected two consecutive operators after ternary else.");
- return nullptr;
- }
-
- op->arguments.push_back(expression[next_op + 1].node); //next expression goes as first
- op->arguments.push_back(expression[next_op - 1].node); //left expression goes as when-true
- op->arguments.push_back(expression[next_op + 3].node); //expression after next goes as when-false
+ if (match(GDScriptTokenizer::Token::EQUAL)) {
+ // Initializer.
+ variable->initializer = parse_expression(false);
+ variable->assignments++;
+ }
- //replace all 3 nodes by this operator and make it an expression
- expression.write[next_op - 1].node = op;
- expression.remove(next_op);
- expression.remove(next_op);
- expression.remove(next_op);
- expression.remove(next_op);
+ if (p_allow_property && match(GDScriptTokenizer::Token::COLON)) {
+ if (match(GDScriptTokenizer::Token::NEWLINE)) {
+ return parse_property(variable, true);
} else {
- if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug...");
- ERR_FAIL_V(nullptr);
- }
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = expression[next_op].op;
- op->line = op_line; //line might have been changed from a \n
-
- if (expression[next_op - 1].is_op) {
- _set_error("Parser bug...");
- ERR_FAIL_V(nullptr);
- }
-
- if (expression[next_op + 1].is_op) {
- // this is not invalid and can really appear
- // but it becomes invalid anyway because no binary op
- // can be followed by a unary op in a valid combination,
- // due to how precedence works, unaries will always disappear first
-
- _set_error("Unexpected two consecutive operators.");
- return nullptr;
- }
-
- op->arguments.push_back(expression[next_op - 1].node); //expression goes as left
- op->arguments.push_back(expression[next_op + 1].node); //next expression goes as right
-
- //replace all 3 nodes by this operator and make it an expression
- expression.write[next_op - 1].node = op;
- expression.remove(next_op);
- expression.remove(next_op);
+ return parse_property(variable, false);
}
}
- return expression[0].node;
-}
-
-GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to_const) {
- switch (p_node->type) {
- case Node::TYPE_BUILT_IN_FUNCTION: {
- //many may probably be optimizable
- return p_node;
- } break;
- case Node::TYPE_ARRAY: {
- ArrayNode *an = static_cast<ArrayNode *>(p_node);
- bool all_constants = true;
-
- for (int i = 0; i < an->elements.size(); i++) {
- an->elements.write[i] = _reduce_expression(an->elements[i], p_to_const);
- if (an->elements[i]->type != Node::TYPE_CONSTANT) {
- all_constants = false;
- }
- }
-
- if (all_constants && p_to_const) {
- //reduce constant array expression
+ end_statement("variable declaration");
- ConstantNode *cn = alloc_node<ConstantNode>();
- Array arr;
- arr.resize(an->elements.size());
- for (int i = 0; i < an->elements.size(); i++) {
- ConstantNode *acn = static_cast<ConstantNode *>(an->elements[i]);
- arr[i] = acn->value;
- }
- cn->value = arr;
- cn->datatype = _type_from_variant(cn->value);
- return cn;
- }
+ variable->export_info.name = variable->identifier->name;
- return an;
-
- } break;
- case Node::TYPE_DICTIONARY: {
- DictionaryNode *dn = static_cast<DictionaryNode *>(p_node);
- bool all_constants = true;
-
- for (int i = 0; i < dn->elements.size(); i++) {
- dn->elements.write[i].key = _reduce_expression(dn->elements[i].key, p_to_const);
- if (dn->elements[i].key->type != Node::TYPE_CONSTANT) {
- all_constants = false;
- }
- dn->elements.write[i].value = _reduce_expression(dn->elements[i].value, p_to_const);
- if (dn->elements[i].value->type != Node::TYPE_CONSTANT) {
- all_constants = false;
- }
- }
+ return variable;
+}
- if (all_constants && p_to_const) {
- //reduce constant array expression
+GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_variable, bool p_need_indent) {
+ if (p_need_indent) {
+ if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block for property after ":".)")) {
+ return nullptr;
+ }
+ }
- ConstantNode *cn = alloc_node<ConstantNode>();
- Dictionary dict;
- for (int i = 0; i < dn->elements.size(); i++) {
- ConstantNode *key_c = static_cast<ConstantNode *>(dn->elements[i].key);
- ConstantNode *value_c = static_cast<ConstantNode *>(dn->elements[i].value);
+ VariableNode *property = p_variable;
- dict[key_c->value] = value_c->value;
- }
- cn->value = dict;
- cn->datatype = _type_from_variant(cn->value);
- return cn;
- }
+ make_completion_context(COMPLETION_PROPERTY_DECLARATION, property);
- return dn;
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected "get" or "set" for property declaration.)")) {
+ return nullptr;
+ }
- } break;
- case Node::TYPE_OPERATOR: {
- OperatorNode *op = static_cast<OperatorNode *>(p_node);
+ IdentifierNode *function = parse_identifier();
- bool all_constants = true;
- int last_not_constant = -1;
+ if (check(GDScriptTokenizer::Token::EQUAL)) {
+ p_variable->property = VariableNode::PROP_SETGET;
+ } else {
+ p_variable->property = VariableNode::PROP_INLINE;
+ if (!p_need_indent) {
+ push_error("Property with inline code must go to an indented block.");
+ }
+ }
- for (int i = 0; i < op->arguments.size(); i++) {
- op->arguments.write[i] = _reduce_expression(op->arguments[i], p_to_const);
- if (op->arguments[i]->type != Node::TYPE_CONSTANT) {
- all_constants = false;
- last_not_constant = i;
- }
- }
+ bool getter_used = false;
+ bool setter_used = false;
- if (op->op == OperatorNode::OP_IS) {
- //nothing much
- return op;
+ // Run with a loop because order doesn't matter.
+ for (int i = 0; i < 2; i++) {
+ if (function->name == "set") {
+ if (setter_used) {
+ push_error(R"(Properties can only have one setter.)");
+ } else {
+ parse_property_setter(property);
+ setter_used = true;
}
- if (op->op == OperatorNode::OP_PARENT_CALL) {
- //nothing much
- return op;
-
- } else if (op->op == OperatorNode::OP_CALL) {
- //can reduce base type constructors
- if ((op->arguments[0]->type == Node::TYPE_TYPE || (op->arguments[0]->type == Node::TYPE_BUILT_IN_FUNCTION && GDScriptFunctions::is_deterministic(static_cast<BuiltInFunctionNode *>(op->arguments[0])->function))) && last_not_constant == 0) {
- //native type constructor or intrinsic function
- const Variant **vptr = nullptr;
- Vector<Variant *> ptrs;
- if (op->arguments.size() > 1) {
- ptrs.resize(op->arguments.size() - 1);
- for (int i = 0; i < ptrs.size(); i++) {
- ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[i + 1]);
- ptrs.write[i] = &cn->value;
- }
-
- vptr = (const Variant **)&ptrs[0];
- }
-
- Callable::CallError ce;
- Variant v;
-
- if (op->arguments[0]->type == Node::TYPE_TYPE) {
- TypeNode *tn = static_cast<TypeNode *>(op->arguments[0]);
- v = Variant::construct(tn->vtype, vptr, ptrs.size(), ce);
-
- } else {
- GDScriptFunctions::Function func = static_cast<BuiltInFunctionNode *>(op->arguments[0])->function;
- GDScriptFunctions::call(func, vptr, ptrs.size(), v, ce);
- }
-
- if (ce.error != Callable::CallError::CALL_OK) {
- String errwhere;
- if (op->arguments[0]->type == Node::TYPE_TYPE) {
- TypeNode *tn = static_cast<TypeNode *>(op->arguments[0]);
- errwhere = "'" + Variant::get_type_name(tn->vtype) + "' constructor";
-
- } else {
- GDScriptFunctions::Function func = static_cast<BuiltInFunctionNode *>(op->arguments[0])->function;
- errwhere = String("'") + GDScriptFunctions::get_func_name(func) + "' intrinsic function";
- }
-
- switch (ce.error) {
- case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- _set_error("Invalid argument (#" + itos(ce.argument + 1) + ") for " + errwhere + ".");
-
- } break;
- case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
- _set_error("Too many arguments for " + errwhere + ".");
- } break;
- case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
- _set_error("Too few arguments for " + errwhere + ".");
- } break;
- default: {
- _set_error("Invalid arguments for " + errwhere + ".");
-
- } break;
- }
-
- error_line = op->line;
-
- return p_node;
- }
-
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = v;
- cn->datatype = _type_from_variant(v);
- return cn;
- }
-
- return op; //don't reduce yet
-
- } else if (op->op == OperatorNode::OP_YIELD) {
- return op;
-
- } else if (op->op == OperatorNode::OP_INDEX) {
- //can reduce indices into constant arrays or dictionaries
-
- if (all_constants) {
- ConstantNode *ca = static_cast<ConstantNode *>(op->arguments[0]);
- ConstantNode *cb = static_cast<ConstantNode *>(op->arguments[1]);
-
- bool valid;
-
- Variant v = ca->value.get(cb->value, &valid);
- if (!valid) {
- _set_error("invalid index in constant expression");
- error_line = op->line;
- return op;
- }
-
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = v;
- cn->datatype = _type_from_variant(v);
- return cn;
- }
-
- return op;
-
- } else if (op->op == OperatorNode::OP_INDEX_NAMED) {
- if (op->arguments[0]->type == Node::TYPE_CONSTANT && op->arguments[1]->type == Node::TYPE_IDENTIFIER) {
- ConstantNode *ca = static_cast<ConstantNode *>(op->arguments[0]);
- IdentifierNode *ib = static_cast<IdentifierNode *>(op->arguments[1]);
-
- bool valid;
- Variant v = ca->value.get_named(ib->name, &valid);
- if (!valid) {
- _set_error("invalid index '" + String(ib->name) + "' in constant expression");
- error_line = op->line;
- return op;
- }
-
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = v;
- cn->datatype = _type_from_variant(v);
- return cn;
- }
-
- return op;
+ } else if (function->name == "get") {
+ if (getter_used) {
+ push_error(R"(Properties can only have one getter.)");
+ } else {
+ parse_property_getter(property);
+ getter_used = true;
}
+ } else {
+ // TODO: Update message to only have the missing one if it's the case.
+ push_error(R"(Expected "get" or "set" for property declaration.)");
+ }
- //validate assignment (don't assign to constant expression
- switch (op->op) {
- case OperatorNode::OP_ASSIGN:
- case OperatorNode::OP_ASSIGN_ADD:
- case OperatorNode::OP_ASSIGN_SUB:
- case OperatorNode::OP_ASSIGN_MUL:
- case OperatorNode::OP_ASSIGN_DIV:
- case OperatorNode::OP_ASSIGN_MOD:
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- case OperatorNode::OP_ASSIGN_BIT_AND:
- case OperatorNode::OP_ASSIGN_BIT_OR:
- case OperatorNode::OP_ASSIGN_BIT_XOR: {
- if (op->arguments[0]->type == Node::TYPE_CONSTANT) {
- _set_error("Can't assign to constant", tokenizer->get_token_line() - 1);
- error_line = op->line;
- return op;
- } else if (op->arguments[0]->type == Node::TYPE_SELF) {
- _set_error("Can't assign to self.", op->line);
- error_line = op->line;
- return op;
- }
-
- if (op->arguments[0]->type == Node::TYPE_OPERATOR) {
- OperatorNode *on = static_cast<OperatorNode *>(op->arguments[0]);
- if (on->op != OperatorNode::OP_INDEX && on->op != OperatorNode::OP_INDEX_NAMED) {
- _set_error("Can't assign to an expression", tokenizer->get_token_line() - 1);
- error_line = op->line;
- return op;
- }
- }
-
- } break;
- default: {
- break;
- }
- }
- //now se if all are constants
- if (!all_constants) {
- return op; //nothing to reduce from here on
- }
-#define _REDUCE_UNARY(m_vop) \
- bool valid = false; \
- Variant res; \
- Variant::evaluate(m_vop, static_cast<ConstantNode *>(op->arguments[0])->value, Variant(), res, valid); \
- if (!valid) { \
- _set_error("Invalid operand for unary operator"); \
- error_line = op->line; \
- return p_node; \
- } \
- ConstantNode *cn = alloc_node<ConstantNode>(); \
- cn->value = res; \
- cn->datatype = _type_from_variant(res); \
- return cn;
-
-#define _REDUCE_BINARY(m_vop) \
- bool valid = false; \
- Variant res; \
- Variant::evaluate(m_vop, static_cast<ConstantNode *>(op->arguments[0])->value, static_cast<ConstantNode *>(op->arguments[1])->value, res, valid); \
- if (!valid) { \
- _set_error("Invalid operands for operator"); \
- error_line = op->line; \
- return p_node; \
- } \
- ConstantNode *cn = alloc_node<ConstantNode>(); \
- cn->value = res; \
- cn->datatype = _type_from_variant(res); \
- return cn;
-
- switch (op->op) {
- //unary operators
- case OperatorNode::OP_NEG: {
- _REDUCE_UNARY(Variant::OP_NEGATE);
- } break;
- case OperatorNode::OP_POS: {
- _REDUCE_UNARY(Variant::OP_POSITIVE);
- } break;
- case OperatorNode::OP_NOT: {
- _REDUCE_UNARY(Variant::OP_NOT);
- } break;
- case OperatorNode::OP_BIT_INVERT: {
- _REDUCE_UNARY(Variant::OP_BIT_NEGATE);
- } break;
- //binary operators (in precedence order)
- case OperatorNode::OP_IN: {
- _REDUCE_BINARY(Variant::OP_IN);
- } break;
- case OperatorNode::OP_EQUAL: {
- _REDUCE_BINARY(Variant::OP_EQUAL);
- } break;
- case OperatorNode::OP_NOT_EQUAL: {
- _REDUCE_BINARY(Variant::OP_NOT_EQUAL);
- } break;
- case OperatorNode::OP_LESS: {
- _REDUCE_BINARY(Variant::OP_LESS);
- } break;
- case OperatorNode::OP_LESS_EQUAL: {
- _REDUCE_BINARY(Variant::OP_LESS_EQUAL);
- } break;
- case OperatorNode::OP_GREATER: {
- _REDUCE_BINARY(Variant::OP_GREATER);
- } break;
- case OperatorNode::OP_GREATER_EQUAL: {
- _REDUCE_BINARY(Variant::OP_GREATER_EQUAL);
- } break;
- case OperatorNode::OP_AND: {
- _REDUCE_BINARY(Variant::OP_AND);
- } break;
- case OperatorNode::OP_OR: {
- _REDUCE_BINARY(Variant::OP_OR);
- } break;
- case OperatorNode::OP_ADD: {
- _REDUCE_BINARY(Variant::OP_ADD);
- } break;
- case OperatorNode::OP_SUB: {
- _REDUCE_BINARY(Variant::OP_SUBTRACT);
- } break;
- case OperatorNode::OP_MUL: {
- _REDUCE_BINARY(Variant::OP_MULTIPLY);
- } break;
- case OperatorNode::OP_DIV: {
- _REDUCE_BINARY(Variant::OP_DIVIDE);
- } break;
- case OperatorNode::OP_MOD: {
- _REDUCE_BINARY(Variant::OP_MODULE);
- } break;
- case OperatorNode::OP_SHIFT_LEFT: {
- _REDUCE_BINARY(Variant::OP_SHIFT_LEFT);
- } break;
- case OperatorNode::OP_SHIFT_RIGHT: {
- _REDUCE_BINARY(Variant::OP_SHIFT_RIGHT);
- } break;
- case OperatorNode::OP_BIT_AND: {
- _REDUCE_BINARY(Variant::OP_BIT_AND);
- } break;
- case OperatorNode::OP_BIT_OR: {
- _REDUCE_BINARY(Variant::OP_BIT_OR);
- } break;
- case OperatorNode::OP_BIT_XOR: {
- _REDUCE_BINARY(Variant::OP_BIT_XOR);
- } break;
- case OperatorNode::OP_TERNARY_IF: {
- if (static_cast<ConstantNode *>(op->arguments[0])->value.booleanize()) {
- return op->arguments[1];
- } else {
- return op->arguments[2];
+ if (i == 0 && p_variable->property == VariableNode::PROP_SETGET) {
+ if (match(GDScriptTokenizer::Token::COMMA)) {
+ // Consume potential newline.
+ if (match(GDScriptTokenizer::Token::NEWLINE)) {
+ if (!p_need_indent) {
+ push_error(R"(Inline setter/getter setting cannot span across multiple lines (use "\\"" if needed).)");
}
- } break;
- default: {
- ERR_FAIL_V(op);
}
- }
-
- } break;
- default: {
- return p_node;
- } break;
- }
-}
-
-GDScriptParser::Node *GDScriptParser::_parse_and_reduce_expression(Node *p_parent, bool p_static, bool p_reduce_const, bool p_allow_assign) {
- Node *expr = _parse_expression(p_parent, p_static, p_allow_assign, p_reduce_const);
- if (!expr || error_set) {
- return nullptr;
- }
- expr = _reduce_expression(expr, p_reduce_const);
- if (!expr || error_set) {
- return nullptr;
- }
- return expr;
-}
-
-bool GDScriptParser::_reduce_export_var_type(Variant &p_value, int p_line) {
- if (p_value.get_type() == Variant::ARRAY) {
- Array arr = p_value;
- for (int i = 0; i < arr.size(); i++) {
- if (!_reduce_export_var_type(arr[i], p_line)) {
- return false;
+ } else {
+ break;
}
}
- return true;
- }
- if (p_value.get_type() == Variant::DICTIONARY) {
- Dictionary dict = p_value;
- for (int i = 0; i < dict.size(); i++) {
- Variant value = dict.get_value_at_index(i);
- if (!_reduce_export_var_type(value, p_line)) {
- return false;
- }
+ if (!match(GDScriptTokenizer::Token::IDENTIFIER)) {
+ break;
}
- return true;
+ function = parse_identifier();
}
- // validate type
- DataType type = _type_from_variant(p_value);
- if (type.kind == DataType::BUILTIN) {
- return true;
- } else if (type.kind == DataType::NATIVE) {
- if (ClassDB::is_parent_class(type.native_type, "Resource")) {
- return true;
- }
+ if (p_variable->property == VariableNode::PROP_SETGET) {
+ end_statement("property declaration");
}
- _set_error("Invalid export type. Only built-in and native resource types can be exported.", p_line);
- return false;
-}
-bool GDScriptParser::_recover_from_completion() {
- if (!completion_found) {
- return false; //can't recover if no completion
+ if (p_need_indent) {
+ consume(GDScriptTokenizer::Token::DEDENT, R"(Expected end of indented block for property.)");
}
- //skip stuff until newline
- while (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE && tokenizer->get_token() != GDScriptTokenizer::TK_EOF && tokenizer->get_token() != GDScriptTokenizer::TK_ERROR) {
- tokenizer->advance();
- }
- completion_found = false;
- error_set = false;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_ERROR) {
- error_set = true;
- }
-
- return true;
+ return property;
}
-GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) {
- PatternNode *pattern = alloc_node<PatternNode>();
-
- GDScriptTokenizer::Token token = tokenizer->get_token();
- if (error_set) {
- return nullptr;
- }
-
- if (token == GDScriptTokenizer::TK_EOF) {
- return nullptr;
- }
-
- switch (token) {
- // array
- case GDScriptTokenizer::TK_BRACKET_OPEN: {
- tokenizer->advance();
- pattern->pt_type = GDScriptParser::PatternNode::PT_ARRAY;
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_CLOSE) {
- tokenizer->advance();
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD && tokenizer->get_token(1) == GDScriptTokenizer::TK_PERIOD) {
- // match everything
- tokenizer->advance(2);
- PatternNode *sub_pattern = alloc_node<PatternNode>();
- sub_pattern->pt_type = GDScriptParser::PatternNode::PT_IGNORE_REST;
- pattern->array.push_back(sub_pattern);
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA && tokenizer->get_token(1) == GDScriptTokenizer::TK_BRACKET_CLOSE) {
- tokenizer->advance(2);
- break;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_CLOSE) {
- tokenizer->advance(1);
- break;
- } else {
- _set_error("'..' pattern only allowed at the end of an array pattern");
- return nullptr;
- }
- }
-
- PatternNode *sub_pattern = _parse_pattern(p_static);
- if (!sub_pattern) {
- return nullptr;
- }
-
- pattern->array.push_back(sub_pattern);
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- continue;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_BRACKET_CLOSE) {
- tokenizer->advance();
- break;
- } else {
- _set_error("Not a valid pattern");
- return nullptr;
- }
- }
- } break;
- // bind
- case GDScriptTokenizer::TK_PR_VAR: {
- tokenizer->advance();
- if (!tokenizer->is_token_literal()) {
- _set_error("Expected identifier for binding variable name.");
- return nullptr;
+void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
+ switch (p_variable->property) {
+ case VariableNode::PROP_INLINE:
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
+ p_variable->setter_parameter = parse_identifier();
}
- pattern->pt_type = GDScriptParser::PatternNode::PT_BIND;
- pattern->bind = tokenizer->get_token_literal();
- // Check if variable name is already used
- BlockNode *bl = current_block;
- while (bl) {
- if (bl->variables.has(pattern->bind)) {
- _set_error("Binding name of '" + pattern->bind.operator String() + "' is already declared in this scope.");
- return nullptr;
- }
- bl = bl->parent_block;
- }
- // Create local variable for proper identifier detection later
- LocalVarNode *lv = alloc_node<LocalVarNode>();
- lv->name = pattern->bind;
- current_block->variables.insert(lv->name, lv);
- tokenizer->advance();
- } break;
- // dictionary
- case GDScriptTokenizer::TK_CURLY_BRACKET_OPEN: {
- tokenizer->advance();
- pattern->pt_type = GDScriptParser::PatternNode::PT_DICTIONARY;
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURLY_BRACKET_CLOSE) {
- tokenizer->advance();
- break;
- }
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
+ consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD && tokenizer->get_token(1) == GDScriptTokenizer::TK_PERIOD) {
- // match everything
- tokenizer->advance(2);
- PatternNode *sub_pattern = alloc_node<PatternNode>();
- sub_pattern->pt_type = PatternNode::PT_IGNORE_REST;
- pattern->array.push_back(sub_pattern);
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA && tokenizer->get_token(1) == GDScriptTokenizer::TK_CURLY_BRACKET_CLOSE) {
- tokenizer->advance(2);
- break;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CURLY_BRACKET_CLOSE) {
- tokenizer->advance(1);
- break;
- } else {
- _set_error("'..' pattern only allowed at the end of a dictionary pattern");
- return nullptr;
- }
- }
-
- Node *key = _parse_and_reduce_expression(pattern, p_static);
- if (!key) {
- _set_error("Not a valid key in pattern");
- return nullptr;
- }
-
- if (key->type != GDScriptParser::Node::TYPE_CONSTANT) {
- _set_error("Not a constant expression as key");
- return nullptr;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
- tokenizer->advance();
-
- PatternNode *value = _parse_pattern(p_static);
- if (!value) {
- _set_error("Expected pattern in dictionary value");
- return nullptr;
- }
-
- pattern->dictionary.insert(static_cast<ConstantNode *>(key), value);
- } else {
- pattern->dictionary.insert(static_cast<ConstantNode *>(key), nullptr);
- }
+ p_variable->setter = parse_suite("setter definition");
+ break;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- continue;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CURLY_BRACKET_CLOSE) {
- tokenizer->advance();
- break;
- } else {
- _set_error("Not a valid pattern");
- return nullptr;
- }
+ case VariableNode::PROP_SETGET:
+ consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "set")");
+ make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected setter function name after "=".)")) {
+ p_variable->setter_pointer = parse_identifier();
}
- } break;
- case GDScriptTokenizer::TK_WILDCARD: {
- tokenizer->advance();
- pattern->pt_type = PatternNode::PT_WILDCARD;
- } break;
- // all the constants like strings and numbers
- default: {
- Node *value = _parse_and_reduce_expression(pattern, p_static);
- if (!value) {
- _set_error("Expect constant expression or variables in a pattern");
- return nullptr;
- }
-
- if (value->type == Node::TYPE_OPERATOR) {
- // Maybe it's SomeEnum.VALUE
- Node *current_value = value;
-
- while (current_value->type == Node::TYPE_OPERATOR) {
- OperatorNode *op_node = static_cast<OperatorNode *>(current_value);
-
- if (op_node->op != OperatorNode::OP_INDEX_NAMED) {
- _set_error("Invalid operator in pattern. Only index (`A.B`) is allowed");
- return nullptr;
- }
- current_value = op_node->arguments[0];
- }
+ break;
+ case VariableNode::PROP_NONE:
+ break; // Unreachable.
+ }
+}
- if (current_value->type != Node::TYPE_IDENTIFIER) {
- _set_error("Only constant expression or variables allowed in a pattern");
- return nullptr;
- }
+void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
+ switch (p_variable->property) {
+ case VariableNode::PROP_INLINE:
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
- } else if (value->type != Node::TYPE_IDENTIFIER && value->type != Node::TYPE_CONSTANT) {
- _set_error("Only constant expressions or variables allowed in a pattern");
- return nullptr;
+ p_variable->getter = parse_suite("getter definition");
+ break;
+ case VariableNode::PROP_SETGET:
+ consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "get")");
+ make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected getter function name after "=".)")) {
+ p_variable->getter_pointer = parse_identifier();
}
-
- pattern->pt_type = PatternNode::PT_CONSTANT;
- pattern->constant = value;
- } break;
+ break;
+ case VariableNode::PROP_NONE:
+ break; // Unreachable.
}
-
- return pattern;
}
-void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static) {
- IndentLevel current_level = indent_level.back()->get();
-
- p_block->has_return = true;
-
- bool catch_all_appeared = false;
-
- while (true) {
- while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE && _parse_newline()) {
- ;
- }
-
- // GDScriptTokenizer::Token token = tokenizer->get_token();
- if (error_set) {
- return;
- }
-
- if (current_level.indent > indent_level.back()->get().indent) {
- break; // go back a level
- }
-
- pending_newline = -1;
-
- PatternBranchNode *branch = alloc_node<PatternBranchNode>();
- branch->body = alloc_node<BlockNode>();
- branch->body->parent_block = p_block;
- p_block->sub_blocks.push_back(branch->body);
- current_block = branch->body;
-
- branch->patterns.push_back(_parse_pattern(p_static));
- if (!branch->patterns[0]) {
- break;
- }
+GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
+ return nullptr;
+ }
- bool has_binding = branch->patterns[0]->pt_type == PatternNode::PT_BIND;
- bool catch_all = has_binding || branch->patterns[0]->pt_type == PatternNode::PT_WILDCARD;
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->identifier = parse_identifier();
-#ifdef DEBUG_ENABLED
- // Branches after a wildcard or binding are unreachable
- if (catch_all_appeared && !current_function->has_unreachable_code) {
- _add_warning(GDScriptWarning::UNREACHABLE_CODE, -1, current_function->name.operator String());
- current_function->has_unreachable_code = true;
+ if (match(GDScriptTokenizer::Token::COLON)) {
+ if (check((GDScriptTokenizer::Token::EQUAL))) {
+ // Infer type.
+ constant->infer_datatype = true;
+ } else {
+ // Parse type.
+ constant->datatype_specifier = parse_type();
}
-#endif
-
- while (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- branch->patterns.push_back(_parse_pattern(p_static));
- if (!branch->patterns[branch->patterns.size() - 1]) {
- return;
- }
+ }
- PatternNode::PatternType pt = branch->patterns[branch->patterns.size() - 1]->pt_type;
+ if (consume(GDScriptTokenizer::Token::EQUAL, R"(Expected initializer after constant name.)")) {
+ // Initializer.
+ constant->initializer = parse_expression(false);
- if (pt == PatternNode::PT_BIND) {
- _set_error("Cannot use bindings with multipattern.");
- return;
- }
-
- catch_all = catch_all || pt == PatternNode::PT_WILDCARD;
+ if (constant->initializer == nullptr) {
+ push_error(R"(Expected initializer expression for constant.)");
+ return nullptr;
}
+ }
- catch_all_appeared = catch_all_appeared || catch_all;
+ end_statement("constant declaration");
- if (!_enter_indent_block()) {
- _set_error("Expected block in pattern branch");
- return;
- }
+ return constant;
+}
- _parse_block(branch->body, p_static);
+GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name.)")) {
+ return nullptr;
+ }
- current_block = p_block;
+ ParameterNode *parameter = alloc_node<ParameterNode>();
+ parameter->identifier = parse_identifier();
- if (!branch->body->has_return) {
- p_block->has_return = false;
+ if (match(GDScriptTokenizer::Token::COLON)) {
+ if (check((GDScriptTokenizer::Token::EQUAL))) {
+ // Infer type.
+ parameter->infer_datatype = true;
+ } else {
+ // Parse type.
+ make_completion_context(COMPLETION_TYPE_NAME, parameter);
+ parameter->datatype_specifier = parse_type();
}
-
- p_branches.push_back(branch);
}
- // Even if all branches return, there is possibility of default fallthrough
- if (!catch_all_appeared) {
- p_block->has_return = false;
+ if (match(GDScriptTokenizer::Token::EQUAL)) {
+ // Default value.
+ parameter->default_value = parse_expression(false);
}
-}
-void GDScriptParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings) {
- const DataType &to_match_type = p_node_to_match->get_datatype();
+ return parameter;
+}
- switch (p_pattern->pt_type) {
- case PatternNode::PT_CONSTANT: {
- DataType pattern_type = _reduce_node_type(p_pattern->constant);
- if (error_set) {
- return;
- }
+GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) {
+ return nullptr;
+ }
- OperatorNode *type_comp = nullptr;
+ SignalNode *signal = alloc_node<SignalNode>();
+ signal->identifier = parse_identifier();
- // static type check if possible
- if (pattern_type.has_type && to_match_type.has_type) {
- if (!_is_type_compatible(to_match_type, pattern_type) && !_is_type_compatible(pattern_type, to_match_type)) {
- _set_error("The pattern type (" + pattern_type.to_string() + ") isn't compatible with the type of the value to match (" + to_match_type.to_string() + ").",
- p_pattern->line);
- return;
- }
- } else {
- // runtime typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
-
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
-
- OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
- typeof_pattern_value->op = OperatorNode::OP_CALL;
- typeof_pattern_value->arguments.push_back(typeof_node);
- typeof_pattern_value->arguments.push_back(p_pattern->constant);
-
- type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_pattern_value);
+ if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
+ do {
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Allow for trailing comma.
+ break;
}
- // compare the actual values
- OperatorNode *value_comp = alloc_node<OperatorNode>();
- value_comp->op = OperatorNode::OP_EQUAL;
- value_comp->arguments.push_back(p_pattern->constant);
- value_comp->arguments.push_back(p_node_to_match);
-
- if (type_comp) {
- OperatorNode *full_comparison = alloc_node<OperatorNode>();
- full_comparison->op = OperatorNode::OP_AND;
- full_comparison->arguments.push_back(type_comp);
- full_comparison->arguments.push_back(value_comp);
-
- p_resulting_node = full_comparison;
- } else {
- p_resulting_node = value_comp;
+ ParameterNode *parameter = parse_parameter();
+ if (parameter == nullptr) {
+ push_error("Expected signal parameter name.");
+ break;
}
-
- } break;
- case PatternNode::PT_BIND: {
- p_bindings[p_pattern->bind] = p_node_to_match;
-
- // a bind always matches
- ConstantNode *true_value = alloc_node<ConstantNode>();
- true_value->value = Variant(true);
- true_value->datatype = _type_from_variant(true_value->value);
- p_resulting_node = true_value;
- } break;
- case PatternNode::PT_ARRAY: {
- bool open_ended = false;
-
- if (p_pattern->array.size() > 0) {
- if (p_pattern->array[p_pattern->array.size() - 1]->pt_type == PatternNode::PT_IGNORE_REST) {
- open_ended = true;
- }
+ if (parameter->default_value != nullptr) {
+ push_error(R"(Signal parameters cannot have a default value.)");
}
-
- // typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() >= length
- // typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() == length
-
- {
- OperatorNode *type_comp = nullptr;
- // static type check if possible
- if (to_match_type.has_type) {
- // must be an array
- if (to_match_type.kind != DataType::BUILTIN || to_match_type.builtin_type != Variant::ARRAY) {
- _set_error("Cannot match an array pattern with a non-array expression.", p_pattern->line);
- return;
- }
- } else {
- // runtime typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
-
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
-
- IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
- typeof_array->name = "TYPE_ARRAY";
-
- type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_array);
- }
-
- // size
- ConstantNode *length = alloc_node<ConstantNode>();
- length->value = Variant(open_ended ? p_pattern->array.size() - 1 : p_pattern->array.size());
- length->datatype = _type_from_variant(length->value);
-
- OperatorNode *call = alloc_node<OperatorNode>();
- call->op = OperatorNode::OP_CALL;
- call->arguments.push_back(p_node_to_match);
-
- IdentifierNode *size = alloc_node<IdentifierNode>();
- size->name = "size";
- call->arguments.push_back(size);
-
- OperatorNode *length_comparison = alloc_node<OperatorNode>();
- length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
- length_comparison->arguments.push_back(call);
- length_comparison->arguments.push_back(length);
-
- if (type_comp) {
- OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
- type_and_length_comparison->op = OperatorNode::OP_AND;
- type_and_length_comparison->arguments.push_back(type_comp);
- type_and_length_comparison->arguments.push_back(length_comparison);
-
- p_resulting_node = type_and_length_comparison;
- } else {
- p_resulting_node = length_comparison;
- }
+ if (signal->parameters_indices.has(parameter->identifier->name)) {
+ push_error(vformat(R"(Parameter with name "%s" was already declared for this signal.)", parameter->identifier->name));
+ } else {
+ signal->parameters_indices[parameter->identifier->name] = signal->parameters.size();
+ signal->parameters.push_back(parameter);
}
+ } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
- for (int i = 0; i < p_pattern->array.size(); i++) {
- PatternNode *pattern = p_pattern->array[i];
-
- Node *condition = nullptr;
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*");
+ }
- ConstantNode *index = alloc_node<ConstantNode>();
- index->value = Variant(i);
- index->datatype = _type_from_variant(index->value);
+ end_statement("signal declaration");
- OperatorNode *indexed_value = alloc_node<OperatorNode>();
- indexed_value->op = OperatorNode::OP_INDEX;
- indexed_value->arguments.push_back(p_node_to_match);
- indexed_value->arguments.push_back(index);
+ return signal;
+}
- _generate_pattern(pattern, indexed_value, condition, p_bindings);
+GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
+ EnumNode *enum_node = alloc_node<EnumNode>();
+ bool named = false;
- // concatenate all the patterns with &&
- OperatorNode *and_node = alloc_node<OperatorNode>();
- and_node->op = OperatorNode::OP_AND;
- and_node->arguments.push_back(p_resulting_node);
- and_node->arguments.push_back(condition);
+ if (check(GDScriptTokenizer::Token::IDENTIFIER)) {
+ advance();
+ enum_node->identifier = parse_identifier();
+ named = true;
+ }
- p_resulting_node = and_node;
- }
+ push_multiline(true);
+ consume(GDScriptTokenizer::Token::BRACE_OPEN, vformat(R"(Expected "{" after %s.)", named ? "enum name" : R"("enum")"));
- } break;
- case PatternNode::PT_DICTIONARY: {
- bool open_ended = false;
+ HashMap<StringName, int> elements;
- if (p_pattern->array.size() > 0) {
- open_ended = true;
- }
+ do {
+ if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
+ break; // Allow trailing comma.
+ }
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
+ EnumNode::Value item;
+ item.identifier = parse_identifier();
+ item.parent_enum = enum_node;
+ item.line = previous.start_line;
+ item.leftmost_column = previous.leftmost_column;
- // typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() >= length
- // typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() == length
-
- {
- OperatorNode *type_comp = nullptr;
- // static type check if possible
- if (to_match_type.has_type) {
- // must be an dictionary
- if (to_match_type.kind != DataType::BUILTIN || to_match_type.builtin_type != Variant::DICTIONARY) {
- _set_error("Cannot match an dictionary pattern with a non-dictionary expression.", p_pattern->line);
- return;
+ if (elements.has(item.identifier->name)) {
+ push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
+ } else if (!named) {
+ // TODO: Abstract this recursive member check.
+ ClassNode *parent = current_class;
+ while (parent != nullptr) {
+ if (parent->members_indices.has(item.identifier->name)) {
+ push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, parent->get_member(item.identifier->name).get_type_name()));
+ break;
}
- } else {
- // runtime typecheck
- BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
- typeof_node->function = GDScriptFunctions::TYPE_OF;
-
- OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
- typeof_match_value->op = OperatorNode::OP_CALL;
- typeof_match_value->arguments.push_back(typeof_node);
- typeof_match_value->arguments.push_back(p_node_to_match);
-
- IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
- typeof_dictionary->name = "TYPE_DICTIONARY";
-
- type_comp = alloc_node<OperatorNode>();
- type_comp->op = OperatorNode::OP_EQUAL;
- type_comp->arguments.push_back(typeof_match_value);
- type_comp->arguments.push_back(typeof_dictionary);
+ parent = parent->outer;
}
+ }
- // size
- ConstantNode *length = alloc_node<ConstantNode>();
- length->value = Variant(open_ended ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());
- length->datatype = _type_from_variant(length->value);
-
- OperatorNode *call = alloc_node<OperatorNode>();
- call->op = OperatorNode::OP_CALL;
- call->arguments.push_back(p_node_to_match);
-
- IdentifierNode *size = alloc_node<IdentifierNode>();
- size->name = "size";
- call->arguments.push_back(size);
-
- OperatorNode *length_comparison = alloc_node<OperatorNode>();
- length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
- length_comparison->arguments.push_back(call);
- length_comparison->arguments.push_back(length);
-
- if (type_comp) {
- OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
- type_and_length_comparison->op = OperatorNode::OP_AND;
- type_and_length_comparison->arguments.push_back(type_comp);
- type_and_length_comparison->arguments.push_back(length_comparison);
+ elements[item.identifier->name] = item.line;
- p_resulting_node = type_and_length_comparison;
- } else {
- p_resulting_node = length_comparison;
+ if (match(GDScriptTokenizer::Token::EQUAL)) {
+ ExpressionNode *value = parse_expression(false);
+ if (value == nullptr) {
+ push_error(R"(Expected expression value after "=".)");
}
+ item.custom_value = value;
}
+ item.rightmost_column = previous.rightmost_column;
- for (Map<ConstantNode *, PatternNode *>::Element *e = p_pattern->dictionary.front(); e; e = e->next()) {
- Node *condition = nullptr;
-
- // check for has, then for pattern
-
- IdentifierNode *has = alloc_node<IdentifierNode>();
- has->name = "has";
-
- OperatorNode *has_call = alloc_node<OperatorNode>();
- has_call->op = OperatorNode::OP_CALL;
- has_call->arguments.push_back(p_node_to_match);
- has_call->arguments.push_back(has);
- has_call->arguments.push_back(e->key());
-
- if (e->value()) {
- OperatorNode *indexed_value = alloc_node<OperatorNode>();
- indexed_value->op = OperatorNode::OP_INDEX;
- indexed_value->arguments.push_back(p_node_to_match);
- indexed_value->arguments.push_back(e->key());
-
- _generate_pattern(e->value(), indexed_value, condition, p_bindings);
-
- OperatorNode *has_and_pattern = alloc_node<OperatorNode>();
- has_and_pattern->op = OperatorNode::OP_AND;
- has_and_pattern->arguments.push_back(has_call);
- has_and_pattern->arguments.push_back(condition);
-
- condition = has_and_pattern;
-
- } else {
- condition = has_call;
- }
+ item.index = enum_node->values.size();
+ enum_node->values.push_back(item);
+ if (!named) {
+ // Add as member of current class.
+ current_class->add_member(item);
+ }
+ }
+ } while (match(GDScriptTokenizer::Token::COMMA));
- // concatenate all the patterns with &&
- OperatorNode *and_node = alloc_node<OperatorNode>();
- and_node->op = OperatorNode::OP_AND;
- and_node->arguments.push_back(p_resulting_node);
- and_node->arguments.push_back(condition);
+ pop_multiline();
+ consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" for enum.)");
- p_resulting_node = and_node;
- }
+ end_statement("enum");
- } break;
- case PatternNode::PT_IGNORE_REST:
- case PatternNode::PT_WILDCARD: {
- // simply generate a `true`
- ConstantNode *true_value = alloc_node<ConstantNode>();
- true_value->value = Variant(true);
- true_value->datatype = _type_from_variant(true_value->value);
- p_resulting_node = true_value;
- } break;
- default: {
- } break;
- }
+ return enum_node;
}
-void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) {
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = "#match_value";
- id->line = p_match_statement->line;
- id->datatype = _reduce_node_type(p_match_statement->val_to_match);
- if (id->datatype.has_type) {
- _mark_line_as_safe(id->line);
- } else {
- _mark_line_as_unsafe(id->line);
+GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
+ bool _static = false;
+ if (previous.type == GDScriptTokenizer::Token::STATIC) {
+ // TODO: Improve message if user uses "static" with "var" or "const"
+ if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
+ return nullptr;
+ }
+ _static = true;
}
- if (error_set) {
- return;
- }
+ FunctionNode *function = alloc_node<FunctionNode>();
+ make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
- for (int i = 0; i < p_match_statement->branches.size(); i++) {
- PatternBranchNode *branch = p_match_statement->branches[i];
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
+ return nullptr;
+ }
- MatchNode::CompiledPatternBranch compiled_branch;
- compiled_branch.compiled_pattern = nullptr;
+ FunctionNode *previous_function = current_function;
+ current_function = function;
- Map<StringName, Node *> binding;
+ function->identifier = parse_identifier();
+ function->is_static = _static;
- for (int j = 0; j < branch->patterns.size(); j++) {
- PatternNode *pattern = branch->patterns[j];
- _mark_line_as_safe(pattern->line);
+ push_multiline(true);
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
- Map<StringName, Node *> bindings;
- Node *resulting_node = nullptr;
- _generate_pattern(pattern, id, resulting_node, bindings);
+ SuiteNode *body = alloc_node<SuiteNode>();
+ SuiteNode *previous_suite = current_suite;
+ current_suite = body;
- if (!resulting_node) {
- return;
+ if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
+ bool default_used = false;
+ do {
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Allow for trailing comma.
+ break;
}
-
- if (!binding.empty() && !bindings.empty()) {
- _set_error("Multipatterns can't contain bindings");
- return;
- } else {
- binding = bindings;
+ ParameterNode *parameter = parse_parameter();
+ if (parameter == nullptr) {
+ break;
}
-
- // Result is always a boolean
- DataType resulting_node_type;
- resulting_node_type.has_type = true;
- resulting_node_type.is_constant = true;
- resulting_node_type.kind = DataType::BUILTIN;
- resulting_node_type.builtin_type = Variant::BOOL;
- resulting_node->set_datatype(resulting_node_type);
-
- if (compiled_branch.compiled_pattern) {
- OperatorNode *or_node = alloc_node<OperatorNode>();
- or_node->op = OperatorNode::OP_OR;
- or_node->arguments.push_back(compiled_branch.compiled_pattern);
- or_node->arguments.push_back(resulting_node);
-
- compiled_branch.compiled_pattern = or_node;
+ if (parameter->default_value != nullptr) {
+ default_used = true;
} else {
- // single pattern | first one
- compiled_branch.compiled_pattern = resulting_node;
+ if (default_used) {
+ push_error("Cannot have a mandatory parameters after optional parameters.");
+ continue;
+ }
}
- }
-
- // prepare the body ...hehe
- for (Map<StringName, Node *>::Element *e = binding.front(); e; e = e->next()) {
- if (!branch->body->variables.has(e->key())) {
- _set_error("Parser bug: missing pattern bind variable.", branch->line);
- ERR_FAIL();
+ if (function->parameters_indices.has(parameter->identifier->name)) {
+ push_error(vformat(R"(Parameter with name "%s" was already declared for this function.)", parameter->identifier->name));
+ } else {
+ function->parameters_indices[parameter->identifier->name] = function->parameters.size();
+ function->parameters.push_back(parameter);
+ body->add_local(parameter);
}
-
- LocalVarNode *local_var = branch->body->variables[e->key()];
- local_var->assign = e->value();
- local_var->set_datatype(local_var->assign->get_datatype());
- local_var->assignments++;
-
- IdentifierNode *id2 = alloc_node<IdentifierNode>();
- id2->name = local_var->name;
- id2->datatype = local_var->datatype;
- id2->declared_block = branch->body;
- id2->set_datatype(local_var->assign->get_datatype());
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_ASSIGN;
- op->arguments.push_back(id2);
- op->arguments.push_back(local_var->assign);
- local_var->assign_op = op;
-
- branch->body->statements.push_front(op);
- branch->body->statements.push_front(local_var);
- }
-
- compiled_branch.body = branch->body;
-
- p_match_statement->compiled_pattern_branches.push_back(compiled_branch);
+ } while (match(GDScriptTokenizer::Token::COMMA));
}
-}
-void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
- IndentLevel current_level = indent_level.back()->get();
-
-#ifdef DEBUG_ENABLED
-
- NewLineNode *nl = alloc_node<NewLineNode>();
-
- nl->line = tokenizer->get_token_line();
- p_block->statements.push_back(nl);
-#endif
+ pop_multiline();
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after function parameters.)*");
- bool is_first_line = true;
-
- while (true) {
- if (!is_first_line && indent_level.back()->prev() && indent_level.back()->prev()->get().indent == current_level.indent) {
- if (indent_level.back()->prev()->get().is_mixed(current_level)) {
- _set_error("Mixed tabs and spaces in indentation.");
- return;
- }
- // pythonic single-line expression, don't parse future lines
- indent_level.pop_back();
- p_block->end_line = tokenizer->get_token_line();
- return;
- }
- is_first_line = false;
-
- GDScriptTokenizer::Token token = tokenizer->get_token();
- if (error_set) {
- return;
- }
-
- if (current_level.indent > indent_level.back()->get().indent) {
- p_block->end_line = tokenizer->get_token_line();
- return; //go back a level
- }
-
- if (pending_newline != -1) {
- NewLineNode *nl2 = alloc_node<NewLineNode>();
- nl2->line = pending_newline;
- p_block->statements.push_back(nl2);
- pending_newline = -1;
+ if (match(GDScriptTokenizer::Token::FORWARD_ARROW)) {
+ make_completion_context(COMPLETION_TYPE_NAME_OR_VOID, function);
+ function->return_type = parse_type(true);
+ if (function->return_type == nullptr) {
+ push_error(R"(Expected return type or "void" after "->".)");
}
+ }
-#ifdef DEBUG_ENABLED
- switch (token) {
- case GDScriptTokenizer::TK_EOF:
- case GDScriptTokenizer::TK_ERROR:
- case GDScriptTokenizer::TK_NEWLINE:
- case GDScriptTokenizer::TK_CF_PASS: {
- // will check later
- } break;
- default: {
- if (p_block->has_return && !current_function->has_unreachable_code) {
- _add_warning(GDScriptWarning::UNREACHABLE_CODE, -1, current_function->name.operator String());
- current_function->has_unreachable_code = true;
- }
- } break;
- }
-#endif // DEBUG_ENABLED
- switch (token) {
- case GDScriptTokenizer::TK_EOF: {
- p_block->end_line = tokenizer->get_token_line();
- return; // End of file!
- } break;
- case GDScriptTokenizer::TK_ERROR: {
- return;
- } break;
- case GDScriptTokenizer::TK_NEWLINE: {
- int line = tokenizer->get_token_line();
-
- if (!_parse_newline()) {
- if (!error_set) {
- p_block->end_line = tokenizer->get_token_line();
- pending_newline = p_block->end_line;
- }
- return;
- }
-
- _mark_line_as_safe(line);
- NewLineNode *nl2 = alloc_node<NewLineNode>();
- nl2->line = line;
- p_block->statements.push_back(nl2);
-
- } break;
- case GDScriptTokenizer::TK_CF_PASS: {
- if (tokenizer->get_token(1) != GDScriptTokenizer::TK_SEMICOLON && tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE && tokenizer->get_token(1) != GDScriptTokenizer::TK_EOF) {
- _set_error("Expected \";\" or a line break.");
- return;
- }
- _mark_line_as_safe(tokenizer->get_token_line());
- tokenizer->advance();
- if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) {
- // Ignore semicolon after 'pass'.
- tokenizer->advance();
- }
- } break;
- case GDScriptTokenizer::TK_PR_VAR: {
- // Variable declaration and (eventual) initialization.
-
- tokenizer->advance();
- int var_line = tokenizer->get_token_line();
- if (!tokenizer->is_token_literal(0, true)) {
- _set_error("Expected an identifier for the local variable name.");
- return;
- }
- StringName n = tokenizer->get_token_literal();
- tokenizer->advance();
- if (current_function) {
- for (int i = 0; i < current_function->arguments.size(); i++) {
- if (n == current_function->arguments[i]) {
- _set_error("Variable \"" + String(n) + "\" already defined in the scope (at line " + itos(current_function->line) + ").");
- return;
- }
- }
- }
- BlockNode *check_block = p_block;
- while (check_block) {
- if (check_block->variables.has(n)) {
- _set_error("Variable \"" + String(n) + "\" already defined in the scope (at line " + itos(check_block->variables[n]->line) + ").");
- return;
- }
- check_block = check_block->parent_block;
- }
-
- //must know when the local variable is declared
- LocalVarNode *lv = alloc_node<LocalVarNode>();
- lv->name = n;
- lv->line = var_line;
- p_block->statements.push_back(lv);
-
- Node *assigned = nullptr;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
- if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
- lv->datatype = DataType();
-#ifdef DEBUG_ENABLED
- lv->datatype.infer_type = true;
-#endif
- tokenizer->advance();
- } else if (!_parse_type(lv->datatype)) {
- _set_error("Expected a type for the variable.");
- return;
- }
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
- tokenizer->advance();
- Node *subexpr = _parse_and_reduce_expression(p_block, p_static);
- if (!subexpr) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- lv->assignments++;
- assigned = subexpr;
- } else {
- assigned = _get_default_value_for_type(lv->datatype, var_line);
- }
- //must be added later, to avoid self-referencing.
- p_block->variables.insert(n, lv);
-
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = n;
- id->declared_block = p_block;
- id->line = var_line;
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(assigned);
- op->line = var_line;
- p_block->statements.push_back(op);
- lv->assign_op = op;
- lv->assign = assigned;
-
- if (!_end_statement()) {
- _set_end_statement_error("var");
- return;
- }
-
- } break;
- case GDScriptTokenizer::TK_CF_IF: {
- tokenizer->advance();
-
- Node *condition = _parse_and_reduce_expression(p_block, p_static);
- if (!condition) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- ControlFlowNode *cf_if = alloc_node<ControlFlowNode>();
-
- cf_if->cf_type = ControlFlowNode::CF_IF;
- cf_if->arguments.push_back(condition);
-
- cf_if->body = alloc_node<BlockNode>();
- cf_if->body->parent_block = p_block;
- cf_if->body->if_condition = condition; //helps code completion
-
- p_block->sub_blocks.push_back(cf_if->body);
-
- if (!_enter_indent_block(cf_if->body)) {
- _set_error("Expected an indented block after \"if\".");
- p_block->end_line = tokenizer->get_token_line();
- return;
- }
-
- current_block = cf_if->body;
- _parse_block(cf_if->body, p_static);
- current_block = p_block;
-
- if (error_set) {
- return;
- }
- p_block->statements.push_back(cf_if);
-
- bool all_have_return = cf_if->body->has_return;
- bool have_else = false;
-
- while (true) {
- while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE && _parse_newline()) {
- ;
- }
-
- if (indent_level.back()->get().indent < current_level.indent) { //not at current indent level
- p_block->end_line = tokenizer->get_token_line();
- return;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CF_ELIF) {
- if (indent_level.back()->get().indent > current_level.indent) {
- _set_error("Invalid indentation.");
- return;
- }
-
- tokenizer->advance();
-
- cf_if->body_else = alloc_node<BlockNode>();
- cf_if->body_else->parent_block = p_block;
- p_block->sub_blocks.push_back(cf_if->body_else);
-
- ControlFlowNode *cf_else = alloc_node<ControlFlowNode>();
- cf_else->cf_type = ControlFlowNode::CF_IF;
-
- //condition
- Node *condition2 = _parse_and_reduce_expression(p_block, p_static);
- if (!condition2) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
- cf_else->arguments.push_back(condition2);
- cf_else->cf_type = ControlFlowNode::CF_IF;
-
- cf_if->body_else->statements.push_back(cf_else);
- cf_if = cf_else;
- cf_if->body = alloc_node<BlockNode>();
- cf_if->body->parent_block = p_block;
- p_block->sub_blocks.push_back(cf_if->body);
-
- if (!_enter_indent_block(cf_if->body)) {
- _set_error("Expected an indented block after \"elif\".");
- p_block->end_line = tokenizer->get_token_line();
- return;
- }
-
- current_block = cf_else->body;
- _parse_block(cf_else->body, p_static);
- current_block = p_block;
- if (error_set) {
- return;
- }
-
- all_have_return = all_have_return && cf_else->body->has_return;
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CF_ELSE) {
- if (indent_level.back()->get().indent > current_level.indent) {
- _set_error("Invalid indentation.");
- return;
- }
-
- tokenizer->advance();
- cf_if->body_else = alloc_node<BlockNode>();
- cf_if->body_else->parent_block = p_block;
- p_block->sub_blocks.push_back(cf_if->body_else);
-
- if (!_enter_indent_block(cf_if->body_else)) {
- _set_error("Expected an indented block after \"else\".");
- p_block->end_line = tokenizer->get_token_line();
- return;
- }
- current_block = cf_if->body_else;
- _parse_block(cf_if->body_else, p_static);
- current_block = p_block;
- if (error_set) {
- return;
- }
-
- all_have_return = all_have_return && cf_if->body_else->has_return;
- have_else = true;
-
- break; //after else, exit
-
- } else {
- break;
- }
- }
-
- cf_if->body->has_return = all_have_return;
- // If there's no else block, path out of the if might not have a return
- p_block->has_return = all_have_return && have_else;
-
- } break;
- case GDScriptTokenizer::TK_CF_WHILE: {
- tokenizer->advance();
- Node *condition2 = _parse_and_reduce_expression(p_block, p_static);
- if (!condition2) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- ControlFlowNode *cf_while = alloc_node<ControlFlowNode>();
-
- cf_while->cf_type = ControlFlowNode::CF_WHILE;
- cf_while->arguments.push_back(condition2);
-
- cf_while->body = alloc_node<BlockNode>();
- cf_while->body->parent_block = p_block;
- p_block->sub_blocks.push_back(cf_while->body);
-
- if (!_enter_indent_block(cf_while->body)) {
- _set_error("Expected an indented block after \"while\".");
- p_block->end_line = tokenizer->get_token_line();
- return;
- }
-
- current_block = cf_while->body;
- _parse_block(cf_while->body, p_static);
- current_block = p_block;
- if (error_set) {
- return;
- }
- p_block->has_return = cf_while->body->has_return;
- p_block->statements.push_back(cf_while);
- } break;
- case GDScriptTokenizer::TK_CF_FOR: {
- tokenizer->advance();
-
- if (!tokenizer->is_token_literal(0, true)) {
- _set_error("Identifier expected after \"for\".");
- }
-
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = tokenizer->get_token_identifier();
-
- tokenizer->advance();
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_OP_IN) {
- _set_error("\"in\" expected after identifier.");
- return;
- }
-
- tokenizer->advance();
-
- Node *container = _parse_and_reduce_expression(p_block, p_static);
- if (!container) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- DataType iter_type;
-
- if (container->type == Node::TYPE_OPERATOR) {
- OperatorNode *op = static_cast<OperatorNode *>(container);
- if (op->op == OperatorNode::OP_CALL && op->arguments[0]->type == Node::TYPE_BUILT_IN_FUNCTION && static_cast<BuiltInFunctionNode *>(op->arguments[0])->function == GDScriptFunctions::GEN_RANGE) {
- //iterating a range, so see if range() can be optimized without allocating memory, by replacing it by vectors (which can work as iterable too!)
-
- Vector<Node *> args;
- Vector<double> constants;
-
- bool constant = true;
-
- for (int i = 1; i < op->arguments.size(); i++) {
- args.push_back(op->arguments[i]);
- if (op->arguments[i]->type == Node::TYPE_CONSTANT) {
- ConstantNode *c = static_cast<ConstantNode *>(op->arguments[i]);
- if (c->value.get_type() == Variant::FLOAT || c->value.get_type() == Variant::INT) {
- constants.push_back(c->value);
- } else {
- constant = false;
- }
- } else {
- constant = false;
- }
- }
-
- if (args.size() > 0 && args.size() < 4) {
- if (constant) {
- ConstantNode *cn = alloc_node<ConstantNode>();
- switch (args.size()) {
- case 1:
- cn->value = (int64_t)constants[0];
- break;
- case 2:
- cn->value = Vector2i(constants[0], constants[1]);
- break;
- case 3:
- cn->value = Vector3i(constants[0], constants[1], constants[2]);
- break;
- }
- cn->datatype = _type_from_variant(cn->value);
- container = cn;
- } else {
- OperatorNode *on = alloc_node<OperatorNode>();
- on->op = OperatorNode::OP_CALL;
-
- TypeNode *tn = alloc_node<TypeNode>();
- on->arguments.push_back(tn);
-
- switch (args.size()) {
- case 1:
- tn->vtype = Variant::INT;
- break;
- case 2:
- tn->vtype = Variant::VECTOR2I;
- break;
- case 3:
- tn->vtype = Variant::VECTOR3I;
- break;
- }
-
- for (int i = 0; i < args.size(); i++) {
- on->arguments.push_back(args[i]);
- }
-
- container = on;
- }
- }
-
- iter_type.has_type = true;
- iter_type.kind = DataType::BUILTIN;
- iter_type.builtin_type = Variant::INT;
- }
- }
-
- ControlFlowNode *cf_for = alloc_node<ControlFlowNode>();
-
- cf_for->cf_type = ControlFlowNode::CF_FOR;
- cf_for->arguments.push_back(id);
- cf_for->arguments.push_back(container);
-
- cf_for->body = alloc_node<BlockNode>();
- cf_for->body->parent_block = p_block;
- p_block->sub_blocks.push_back(cf_for->body);
-
- if (!_enter_indent_block(cf_for->body)) {
- _set_error("Expected indented block after \"for\".");
- p_block->end_line = tokenizer->get_token_line();
- return;
- }
-
- current_block = cf_for->body;
-
- // this is for checking variable for redefining
- // inside this _parse_block
- LocalVarNode *lv = alloc_node<LocalVarNode>();
- lv->name = id->name;
- lv->line = id->line;
- lv->assignments++;
- id->declared_block = cf_for->body;
- lv->set_datatype(iter_type);
- id->set_datatype(iter_type);
- cf_for->body->variables.insert(id->name, lv);
- _parse_block(cf_for->body, p_static);
- current_block = p_block;
-
- if (error_set) {
- return;
- }
- p_block->has_return = cf_for->body->has_return;
- p_block->statements.push_back(cf_for);
- } break;
- case GDScriptTokenizer::TK_CF_CONTINUE: {
- _mark_line_as_safe(tokenizer->get_token_line());
- tokenizer->advance();
- ControlFlowNode *cf_continue = alloc_node<ControlFlowNode>();
- cf_continue->cf_type = ControlFlowNode::CF_CONTINUE;
- p_block->statements.push_back(cf_continue);
- if (!_end_statement()) {
- _set_end_statement_error("continue");
- return;
- }
- } break;
- case GDScriptTokenizer::TK_CF_BREAK: {
- _mark_line_as_safe(tokenizer->get_token_line());
- tokenizer->advance();
- ControlFlowNode *cf_break = alloc_node<ControlFlowNode>();
- cf_break->cf_type = ControlFlowNode::CF_BREAK;
- p_block->statements.push_back(cf_break);
- if (!_end_statement()) {
- _set_end_statement_error("break");
- return;
- }
- } break;
- case GDScriptTokenizer::TK_CF_RETURN: {
- tokenizer->advance();
- ControlFlowNode *cf_return = alloc_node<ControlFlowNode>();
- cf_return->cf_type = ControlFlowNode::CF_RETURN;
- cf_return->line = tokenizer->get_token_line(-1);
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON || tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE || tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
- //expect end of statement
- p_block->statements.push_back(cf_return);
- if (!_end_statement()) {
- return;
- }
- } else {
- //expect expression
- Node *retexpr = _parse_and_reduce_expression(p_block, p_static);
- if (!retexpr) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
- cf_return->arguments.push_back(retexpr);
- p_block->statements.push_back(cf_return);
- if (!_end_statement()) {
- _set_end_statement_error("return");
- return;
- }
- }
- p_block->has_return = true;
-
- } break;
- case GDScriptTokenizer::TK_CF_MATCH: {
- tokenizer->advance();
-
- MatchNode *match_node = alloc_node<MatchNode>();
-
- Node *val_to_match = _parse_and_reduce_expression(p_block, p_static);
-
- if (!val_to_match) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- match_node->val_to_match = val_to_match;
-
- if (!_enter_indent_block()) {
- _set_error("Expected indented pattern matching block after \"match\".");
- return;
- }
-
- BlockNode *compiled_branches = alloc_node<BlockNode>();
- compiled_branches->parent_block = p_block;
- compiled_branches->parent_class = p_block->parent_class;
-
- p_block->sub_blocks.push_back(compiled_branches);
-
- _parse_pattern_block(compiled_branches, match_node->branches, p_static);
-
- if (error_set) {
- return;
- }
-
- ControlFlowNode *match_cf_node = alloc_node<ControlFlowNode>();
- match_cf_node->cf_type = ControlFlowNode::CF_MATCH;
- match_cf_node->match = match_node;
- match_cf_node->body = compiled_branches;
-
- p_block->has_return = p_block->has_return || compiled_branches->has_return;
- p_block->statements.push_back(match_cf_node);
+ // TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after function declaration.)");
- _end_statement();
- } break;
- case GDScriptTokenizer::TK_PR_ASSERT: {
- tokenizer->advance();
+ current_suite = previous_suite;
+ function->body = parse_suite("function declaration", body);
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after assert");
- return;
- }
+ current_function = previous_function;
+ return function;
+}
- int assert_line = tokenizer->get_token_line();
+GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_valid_targets) {
+ AnnotationNode *annotation = alloc_node<AnnotationNode>();
- tokenizer->advance();
+ annotation->name = previous.literal;
- Vector<Node *> args;
- const bool result = _parse_arguments(p_block, args, p_static);
- if (!result) {
- return;
- }
+ make_completion_context(COMPLETION_ANNOTATION, annotation);
- if (args.empty() || args.size() > 2) {
- _set_error("Wrong number of arguments, expected 1 or 2", assert_line);
- return;
- }
+ bool valid = true;
- AssertNode *an = alloc_node<AssertNode>();
- an->condition = _reduce_expression(args[0], p_static);
- an->line = assert_line;
+ if (!valid_annotations.has(annotation->name)) {
+ push_error(vformat(R"(Unrecognized annotation: "%s".)", annotation->name));
+ valid = false;
+ }
- if (args.size() == 2) {
- an->message = _reduce_expression(args[1], p_static);
- } else {
- ConstantNode *message_node = alloc_node<ConstantNode>();
- message_node->value = String();
- message_node->datatype = _type_from_variant(message_node->value);
- an->message = message_node;
- }
+ annotation->info = &valid_annotations[annotation->name];
- p_block->statements.push_back(an);
+ if (!annotation->applies_to(p_valid_targets)) {
+ push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name));
+ valid = false;
+ }
- if (!_end_statement()) {
- _set_end_statement_error("assert");
- return;
- }
- } break;
- case GDScriptTokenizer::TK_PR_BREAKPOINT: {
- tokenizer->advance();
- BreakpointNode *bn = alloc_node<BreakpointNode>();
- p_block->statements.push_back(bn);
-
- if (!_end_statement()) {
- _set_end_statement_error("breakpoint");
- return;
- }
- } break;
- default: {
- Node *expression = _parse_and_reduce_expression(p_block, p_static, false, true);
- if (!expression) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
- p_block->statements.push_back(expression);
- if (!_end_statement()) {
- // Attempt to guess a better error message if the user "retypes" a variable
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON && tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
- _set_error("Unexpected ':=', use '=' instead. Expected end of statement after expression.");
- } else {
- _set_error(vformat("Expected end of statement after expression, got %s instead.", tokenizer->get_token_name(tokenizer->get_token())));
- }
- return;
+ if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
+ // Arguments.
+ push_completion_call(annotation);
+ make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true);
+ if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
+ int argument_index = 0;
+ do {
+ make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true);
+ set_last_completion_call_arg(argument_index++);
+ ExpressionNode *argument = parse_expression(false);
+ if (argument == nullptr) {
+ valid = false;
+ continue;
}
+ annotation->arguments.push_back(argument);
+ } while (match(GDScriptTokenizer::Token::COMMA));
- } break;
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after annotation arguments.)*");
}
+ pop_completion_call();
}
-}
-
-bool GDScriptParser::_parse_newline() {
- if (tokenizer->get_token(1) != GDScriptTokenizer::TK_EOF && tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) {
- IndentLevel current_level = indent_level.back()->get();
- int indent = tokenizer->get_token_line_indent();
- int tabs = tokenizer->get_token_line_tab_indent();
- IndentLevel new_level(indent, tabs);
-
- if (new_level.is_mixed(current_level)) {
- _set_error("Mixed tabs and spaces in indentation.");
- return false;
- }
-
- if (indent > current_level.indent) {
- _set_error("Unexpected indentation.");
- return false;
- }
-
- if (indent < current_level.indent) {
- while (indent < current_level.indent) {
- //exit block
- if (indent_level.size() == 1) {
- _set_error("Invalid indentation. Bug?");
- return false;
- }
- indent_level.pop_back();
+ match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional.
- if (indent_level.back()->get().indent < indent) {
- _set_error("Unindent does not match any outer indentation level.");
- return false;
- }
-
- if (indent_level.back()->get().is_mixed(current_level)) {
- _set_error("Mixed tabs and spaces in indentation.");
- return false;
- }
-
- current_level = indent_level.back()->get();
- }
-
- tokenizer->advance();
- return false;
- }
+ if (valid) {
+ valid = validate_annotation_arguments(annotation);
}
- tokenizer->advance();
- return true;
+ return valid ? annotation : nullptr;
}
-void GDScriptParser::_parse_extends(ClassNode *p_class) {
- if (p_class->extends_used) {
- _set_error("\"extends\" can only be present once per script.");
- return;
- }
-
- if (!p_class->constant_expressions.empty() || !p_class->subclasses.empty() || !p_class->functions.empty() || !p_class->variables.empty()) {
- _set_error("\"extends\" must be used before anything else.");
- return;
+void GDScriptParser::clear_unused_annotations() {
+ for (const List<AnnotationNode *>::Element *E = annotation_stack.front(); E != nullptr; E = E->next()) {
+ AnnotationNode *annotation = E->get();
+ push_error(vformat(R"(Annotation "%s" does not precedes a valid target, so it will have no effect.)", annotation->name), annotation);
}
- p_class->extends_used = true;
+ annotation_stack.clear();
+}
- tokenizer->advance();
+bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments, bool p_is_vararg) {
+ ERR_FAIL_COND_V_MSG(valid_annotations.has(p_info.name), false, vformat(R"(Annotation "%s" already registered.)", p_info.name));
- if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE && tokenizer->get_token_type() == Variant::OBJECT) {
- p_class->extends_class.push_back(Variant::get_type_name(Variant::OBJECT));
- tokenizer->advance();
- return;
+ AnnotationInfo new_annotation;
+ new_annotation.info = p_info;
+ new_annotation.info.default_arguments.resize(p_optional_arguments);
+ if (p_is_vararg) {
+ new_annotation.info.flags |= METHOD_FLAG_VARARG;
}
+ new_annotation.apply = p_apply;
+ new_annotation.target_kind = p_target_kinds;
- // see if inheritance happens from a file
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT) {
- Variant constant = tokenizer->get_token_constant();
- if (constant.get_type() != Variant::STRING) {
- _set_error("\"extends\" constant must be a string.");
- return;
- }
+ valid_annotations[p_info.name] = new_annotation;
+ return true;
+}
- p_class->extends_file = constant;
- tokenizer->advance();
+GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite) {
+ SuiteNode *suite = p_suite != nullptr ? p_suite : alloc_node<SuiteNode>();
+ suite->parent_block = current_suite;
+ current_suite = suite;
- // Add parent script as a dependency
- String parent = constant;
- if (parent.is_rel_path()) {
- parent = base_path.plus_file(parent).simplify_path();
- }
- dependencies.push_back(parent);
+ bool multiline = false;
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PERIOD) {
- return;
- } else {
- tokenizer->advance();
- }
+ if (check(GDScriptTokenizer::Token::NEWLINE)) {
+ multiline = true;
}
- while (true) {
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_IDENTIFIER: {
- StringName identifier = tokenizer->get_token_identifier();
- p_class->extends_class.push_back(identifier);
- } break;
-
- case GDScriptTokenizer::TK_PERIOD:
- break;
+ if (multiline) {
+ consume(GDScriptTokenizer::Token::NEWLINE, vformat(R"(Expected newline after %s.)", p_context));
- default: {
- _set_error("Invalid \"extends\" syntax, expected string constant (path) and/or identifier (parent class).");
- return;
- }
- }
-
- tokenizer->advance(1);
-
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_IDENTIFIER:
- case GDScriptTokenizer::TK_PERIOD:
- continue;
-
- default:
- return;
+ if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) {
+ current_suite = suite->parent_block;
+ return suite;
}
}
-}
-
-void GDScriptParser::_parse_class(ClassNode *p_class) {
- IndentLevel current_level = indent_level.back()->get();
- while (true) {
- GDScriptTokenizer::Token token = tokenizer->get_token();
- if (error_set) {
- return;
- }
-
- if (current_level.indent > indent_level.back()->get().indent) {
- p_class->end_line = tokenizer->get_token_line();
- return; //go back a level
+ do {
+ Node *statement = parse_statement();
+ if (statement == nullptr) {
+ continue;
}
+ suite->statements.push_back(statement);
- switch (token) {
- case GDScriptTokenizer::TK_CURSOR: {
- tokenizer->advance();
- } break;
- case GDScriptTokenizer::TK_EOF: {
- p_class->end_line = tokenizer->get_token_line();
- return; // End of file!
- } break;
- case GDScriptTokenizer::TK_ERROR: {
- return; // Go back.
- } break;
- case GDScriptTokenizer::TK_NEWLINE: {
- if (!_parse_newline()) {
- if (!error_set) {
- p_class->end_line = tokenizer->get_token_line();
- }
- return;
- }
- } break;
- case GDScriptTokenizer::TK_PR_EXTENDS: {
- _mark_line_as_safe(tokenizer->get_token_line());
- _parse_extends(p_class);
- if (error_set) {
- return;
- }
- if (!_end_statement()) {
- _set_end_statement_error("extends");
- return;
- }
-
- } break;
- case GDScriptTokenizer::TK_PR_CLASS_NAME: {
- _mark_line_as_safe(tokenizer->get_token_line());
- if (p_class->owner) {
- _set_error("\"class_name\" is only valid for the main class namespace.");
- return;
- }
- if (self_path.begins_with("res://") && self_path.find("::") != -1) {
- _set_error("\"class_name\" isn't allowed in built-in scripts.");
- return;
- }
- if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) {
- _set_error("\"class_name\" syntax: \"class_name <UniqueName>\"");
- return;
- }
- if (p_class->classname_used) {
- _set_error("\"class_name\" can only be present once per script.");
- return;
- }
-
- p_class->classname_used = true;
-
- p_class->name = tokenizer->get_token_identifier(1);
-
- if (self_path != String() && ScriptServer::is_global_class(p_class->name) && ScriptServer::get_global_class_path(p_class->name) != self_path) {
- _set_error("Unique global class \"" + p_class->name + "\" already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
- return;
- }
-
- if (ClassDB::class_exists(p_class->name)) {
- _set_error("The class \"" + p_class->name + "\" shadows a native class.");
- return;
- }
-
- if (p_class->classname_used && ProjectSettings::get_singleton()->has_setting("autoload/" + p_class->name)) {
- const String autoload_path = ProjectSettings::get_singleton()->get_setting("autoload/" + p_class->name);
- if (autoload_path.begins_with("*")) {
- // It's a singleton, and not just a regular AutoLoad script.
- _set_error("The class \"" + p_class->name + "\" conflicts with the AutoLoad singleton of the same name, and is therefore redundant. Remove the class_name declaration to fix this error.");
- }
- return;
+ // Register locals.
+ switch (statement->type) {
+ case Node::VARIABLE: {
+ VariableNode *variable = static_cast<VariableNode *>(statement);
+ const SuiteNode::Local &local = current_suite->get_local(variable->identifier->name);
+ if (local.type != SuiteNode::Local::UNDEFINED) {
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name));
}
-
- tokenizer->advance(2);
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
-
- if ((tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING)) {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- Variant constant = tokenizer->get_token_constant();
- String icon_path = constant.operator String();
-
- String abs_icon_path = icon_path.is_rel_path() ? self_path.get_base_dir().plus_file(icon_path).simplify_path() : icon_path;
- if (!FileAccess::exists(abs_icon_path)) {
- _set_error("No class icon found at: " + abs_icon_path);
- return;
- }
-
- p_class->icon_path = icon_path;
- }
-#endif
-
- tokenizer->advance();
+ current_suite->add_local(variable);
+ break;
+ }
+ case Node::CONSTANT: {
+ ConstantNode *constant = static_cast<ConstantNode *>(statement);
+ const SuiteNode::Local &local = current_suite->get_local(constant->identifier->name);
+ if (local.type != SuiteNode::Local::UNDEFINED) {
+ String name;
+ if (local.type == SuiteNode::Local::CONSTANT) {
+ name = "constant";
} else {
- _set_error("The optional parameter after \"class_name\" must be a string constant file path to an icon.");
- return;
+ name = "variable";
}
-
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT) {
- _set_error("The class icon must be separated by a comma.");
- return;
- }
-
- } break;
- case GDScriptTokenizer::TK_PR_TOOL: {
- if (p_class->tool) {
- _set_error("The \"tool\" keyword can only be present once per script.");
- return;
- }
-
- p_class->tool = true;
- tokenizer->advance();
-
- } break;
- case GDScriptTokenizer::TK_PR_CLASS: {
- //class inside class :D
-
- StringName name;
-
- if (tokenizer->get_token(1) != GDScriptTokenizer::TK_IDENTIFIER) {
- _set_error("\"class\" syntax: \"class <Name>:\" or \"class <Name> extends <BaseClass>:\"");
- return;
- }
- name = tokenizer->get_token_identifier(1);
- tokenizer->advance(2);
-
- // Check if name is shadowing something else
- if (ClassDB::class_exists(name) || ClassDB::class_exists("_" + name.operator String())) {
- _set_error("The class \"" + String(name) + "\" shadows a native class.");
- return;
- }
- if (ScriptServer::is_global_class(name)) {
- _set_error("Can't override name of the unique global class \"" + name + "\". It already exists at: " + ScriptServer::get_global_class_path(p_class->name));
- return;
- }
- ClassNode *outer_class = p_class;
- while (outer_class) {
- for (int i = 0; i < outer_class->subclasses.size(); i++) {
- if (outer_class->subclasses[i]->name == name) {
- _set_error("Another class named \"" + String(name) + "\" already exists in this scope (at line " + itos(outer_class->subclasses[i]->line) + ").");
- return;
- }
- }
- if (outer_class->constant_expressions.has(name)) {
- _set_error("A constant named \"" + String(name) + "\" already exists in the outer class scope (at line" + itos(outer_class->constant_expressions[name].expression->line) + ").");
- return;
- }
- for (int i = 0; i < outer_class->variables.size(); i++) {
- if (outer_class->variables[i].identifier == name) {
- _set_error("A variable named \"" + String(name) + "\" already exists in the outer class scope (at line " + itos(outer_class->variables[i].line) + ").");
- return;
- }
- }
-
- outer_class = outer_class->owner;
- }
-
- ClassNode *newclass = alloc_node<ClassNode>();
- newclass->initializer = alloc_node<BlockNode>();
- newclass->initializer->parent_class = newclass;
- newclass->ready = alloc_node<BlockNode>();
- newclass->ready->parent_class = newclass;
- newclass->name = name;
- newclass->owner = p_class;
-
- p_class->subclasses.push_back(newclass);
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_EXTENDS) {
- _parse_extends(newclass);
- if (error_set) {
- return;
- }
- }
-
- if (!_enter_indent_block()) {
- _set_error("Indented block expected.");
- return;
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name));
}
- current_class = newclass;
- _parse_class(newclass);
- current_class = p_class;
-
- } break;
- /* this is for functions....
- case GDScriptTokenizer::TK_CF_PASS: {
-
- tokenizer->advance(1);
- } break;
- */
- case GDScriptTokenizer::TK_PR_STATIC: {
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
- _set_error("Expected \"func\".");
- return;
- }
-
- [[fallthrough]];
+ current_suite->add_local(constant);
+ break;
}
- case GDScriptTokenizer::TK_PR_FUNCTION: {
- bool _static = false;
- pending_newline = -1;
-
- if (tokenizer->get_token(-1) == GDScriptTokenizer::TK_PR_STATIC) {
- _static = true;
- }
-
- tokenizer->advance();
- StringName name;
+ default:
+ break;
+ }
- if (_get_completable_identifier(COMPLETION_VIRTUAL_FUNC, name)) {
- }
+ } while (multiline && !check(GDScriptTokenizer::Token::DEDENT) && !is_at_end());
- if (name == StringName()) {
- _set_error("Expected an identifier after \"func\" (syntax: \"func <identifier>([arguments]):\").");
- return;
- }
+ if (multiline) {
+ consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
+ }
- for (int i = 0; i < p_class->functions.size(); i++) {
- if (p_class->functions[i]->name == name) {
- _set_error("The function \"" + String(name) + "\" already exists in this class (at line " + itos(p_class->functions[i]->line) + ").");
- }
- }
- for (int i = 0; i < p_class->static_functions.size(); i++) {
- if (p_class->static_functions[i]->name == name) {
- _set_error("The function \"" + String(name) + "\" already exists in this class (at line " + itos(p_class->static_functions[i]->line) + ").");
- }
- }
+ current_suite = suite->parent_block;
+ return suite;
+}
+GDScriptParser::Node *GDScriptParser::parse_statement() {
+ Node *result = nullptr;
#ifdef DEBUG_ENABLED
- if (p_class->constant_expressions.has(name)) {
- _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name);
- }
- for (int i = 0; i < p_class->variables.size(); i++) {
- if (p_class->variables[i].identifier == name) {
- _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name);
- }
- }
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- if (p_class->subclasses[i]->name == name) {
- _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name);
- }
- }
-#endif // DEBUG_ENABLED
+ bool unreachable = current_suite->has_return && !current_suite->has_unreachable_code;
+#endif
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- _set_error("Expected \"(\" after the identifier (syntax: \"func <identifier>([arguments]):\" ).");
- return;
+ switch (current.type) {
+ case GDScriptTokenizer::Token::PASS:
+ advance();
+ result = alloc_node<PassNode>();
+ end_statement(R"("pass")");
+ break;
+ case GDScriptTokenizer::Token::VAR:
+ advance();
+ result = parse_variable();
+ break;
+ case GDScriptTokenizer::Token::CONST:
+ advance();
+ result = parse_constant();
+ break;
+ case GDScriptTokenizer::Token::IF:
+ advance();
+ result = parse_if();
+ break;
+ case GDScriptTokenizer::Token::FOR:
+ advance();
+ result = parse_for();
+ break;
+ case GDScriptTokenizer::Token::WHILE:
+ advance();
+ result = parse_while();
+ break;
+ case GDScriptTokenizer::Token::MATCH:
+ advance();
+ result = parse_match();
+ break;
+ case GDScriptTokenizer::Token::BREAK:
+ advance();
+ result = parse_break();
+ break;
+ case GDScriptTokenizer::Token::CONTINUE:
+ advance();
+ result = parse_continue();
+ break;
+ case GDScriptTokenizer::Token::RETURN: {
+ advance();
+ ReturnNode *n_return = alloc_node<ReturnNode>();
+ if (!is_statement_end()) {
+ if (current_function && current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) {
+ push_error(R"(Constructor cannot return a value.)");
}
+ n_return->return_value = parse_expression(false);
+ }
+ result = n_return;
- tokenizer->advance();
-
- Vector<StringName> arguments;
- Vector<DataType> argument_types;
- Vector<Node *> default_values;
-#ifdef DEBUG_ENABLED
- Vector<int> arguments_usage;
-#endif // DEBUG_ENABLED
-
- int fnline = tokenizer->get_token_line();
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- //has arguments
- bool defaulting = false;
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance();
- continue;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_VAR) {
- tokenizer->advance(); //var before the identifier is allowed
- }
+ current_suite->has_return = true;
- if (!tokenizer->is_token_literal(0, true)) {
- _set_error("Expected an identifier for an argument.");
- return;
- }
+ end_statement("return statement");
+ break;
+ }
+ case GDScriptTokenizer::Token::BREAKPOINT:
+ advance();
+ result = alloc_node<BreakpointNode>();
+ end_statement(R"("breakpoint")");
+ break;
+ case GDScriptTokenizer::Token::ASSERT:
+ advance();
+ result = parse_assert();
+ break;
+ case GDScriptTokenizer::Token::ANNOTATION: {
+ advance();
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT);
+ if (annotation != nullptr) {
+ annotation_stack.push_back(annotation);
+ }
+ break;
+ }
+ default: {
+ // Expression statement.
+ ExpressionNode *expression = parse_expression(true); // Allow assignment here.
+ if (expression == nullptr) {
+ push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
+ }
+ end_statement("expression");
+ result = expression;
- StringName argname = tokenizer->get_token_identifier();
- for (int i = 0; i < arguments.size(); i++) {
- if (arguments[i] == argname) {
- _set_error("The argument name \"" + String(argname) + "\" is defined multiple times.");
- return;
- }
- }
- arguments.push_back(argname);
#ifdef DEBUG_ENABLED
- arguments_usage.push_back(0);
-#endif // DEBUG_ENABLED
-
- tokenizer->advance();
-
- DataType argtype;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
- if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
- argtype.infer_type = true;
- tokenizer->advance();
- } else if (!_parse_type(argtype)) {
- _set_error("Expected a type for an argument.");
- return;
- }
- }
- argument_types.push_back(argtype);
-
- if (defaulting && tokenizer->get_token() != GDScriptTokenizer::TK_OP_ASSIGN) {
- _set_error("Default parameter expected.");
- return;
- }
-
- //tokenizer->advance();
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
- defaulting = true;
- tokenizer->advance(1);
- Node *defval = _parse_and_reduce_expression(p_class, _static);
- if (!defval || error_set) {
- return;
- }
-
- OperatorNode *on = alloc_node<OperatorNode>();
- on->op = OperatorNode::OP_ASSIGN;
- on->line = fnline;
-
- IdentifierNode *in = alloc_node<IdentifierNode>();
- in->name = argname;
- in->line = fnline;
-
- on->arguments.push_back(in);
- on->arguments.push_back(defval);
- /* no ..
- if (defval->type!=Node::TYPE_CONSTANT) {
-
- _set_error("default argument must be constant");
- }
- */
- default_values.push_back(on);
- }
-
- while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance();
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- continue;
- } else if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \",\" or \")\".");
- return;
- }
-
+ if (expression != nullptr) {
+ switch (expression->type) {
+ case Node::CALL:
+ case Node::ASSIGNMENT:
+ case Node::AWAIT:
+ // Fine.
break;
- }
+ default:
+ push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION);
}
+ }
+#endif
+ break;
+ }
+ }
- tokenizer->advance();
-
- BlockNode *block = alloc_node<BlockNode>();
- block->parent_class = p_class;
-
- FunctionNode *function = alloc_node<FunctionNode>();
- function->name = name;
- function->arguments = arguments;
- function->argument_types = argument_types;
- function->default_values = default_values;
- function->_static = _static;
- function->line = fnline;
#ifdef DEBUG_ENABLED
- function->arguments_usage = arguments_usage;
-#endif // DEBUG_ENABLED
- function->rpc_mode = rpc_mode;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
-
- if (name == "_init") {
- if (_static) {
- _set_error("The constructor cannot be static.");
- return;
- }
-
- if (p_class->extends_used) {
- OperatorNode *cparent = alloc_node<OperatorNode>();
- cparent->op = OperatorNode::OP_PARENT_CALL;
- block->statements.push_back(cparent);
-
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = "_init";
- cparent->arguments.push_back(id);
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD) {
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- _set_error("Expected \"(\" for parent constructor arguments.");
- return;
- }
- tokenizer->advance();
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- //has arguments
- parenthesis++;
- while (true) {
- current_function = function;
- Node *arg = _parse_and_reduce_expression(p_class, _static);
- current_function = nullptr;
- cparent->arguments.push_back(arg);
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- continue;
- } else if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \",\" or \")\".");
- return;
- }
-
- break;
- }
- parenthesis--;
- }
-
- tokenizer->advance();
- }
- } else {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PERIOD) {
- _set_error("Parent constructor call found for a class without inheritance.");
- return;
- }
- }
- }
-
- DataType return_type;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_FORWARD_ARROW) {
- if (!_parse_type(return_type, true)) {
- _set_error("Expected a return type for the function.");
- return;
- }
- }
-
- if (!_enter_indent_block(block)) {
- _set_error(vformat("Indented block expected after declaration of \"%s\" function.", function->name));
- return;
- }
-
- function->return_type = return_type;
-
- if (_static) {
- p_class->static_functions.push_back(function);
- } else {
- p_class->functions.push_back(function);
- }
-
- current_function = function;
- function->body = block;
- current_block = block;
- _parse_block(block, _static);
- current_block = nullptr;
-
- //arguments
- } break;
- case GDScriptTokenizer::TK_PR_SIGNAL: {
- tokenizer->advance();
-
- if (!tokenizer->is_token_literal()) {
- _set_error("Expected an identifier after \"signal\".");
- return;
- }
-
- ClassNode::Signal sig;
- sig.name = tokenizer->get_token_identifier();
- sig.emissions = 0;
- sig.line = tokenizer->get_token_line();
- tokenizer->advance();
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
- tokenizer->advance();
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance();
- continue;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- tokenizer->advance();
- break;
- }
-
- if (!tokenizer->is_token_literal(0, true)) {
- _set_error("Expected an identifier in a \"signal\" argument.");
- return;
- }
-
- sig.arguments.push_back(tokenizer->get_token_identifier());
- tokenizer->advance();
-
- while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance();
- }
+ if (unreachable) {
+ current_suite->has_unreachable_code = true;
+ push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name);
+ }
+#endif
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- } else if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \",\" or \")\" after a \"signal\" parameter identifier.");
- return;
- }
- }
- }
+ if (panic_mode) {
+ synchronize();
+ }
- p_class->_signals.push_back(sig);
+ return result;
+}
- if (!_end_statement()) {
- _set_end_statement_error("signal");
- return;
- }
- } break;
- case GDScriptTokenizer::TK_PR_EXPORT: {
- tokenizer->advance();
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
-#define _ADVANCE_AND_CONSUME_NEWLINES \
- do { \
- tokenizer->advance(); \
- } while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE)
-
- _ADVANCE_AND_CONSUME_NEWLINES;
- parenthesis++;
-
- String hint_prefix = "";
- bool is_arrayed = false;
-
- while (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE &&
- tokenizer->get_token_type() == Variant::ARRAY &&
- tokenizer->get_token(1) == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance(); // Array
- tokenizer->advance(); // Comma
- if (is_arrayed) {
- hint_prefix += itos(Variant::ARRAY) + ":";
- } else {
- is_arrayed = true;
- }
- }
+GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
+ // TODO: Add assert message.
+ AssertNode *assert = alloc_node<AssertNode>();
- if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
- Variant::Type type = tokenizer->get_token_type();
- if (type == Variant::NIL) {
- _set_error("Can't export null type.");
- return;
- }
- if (type == Variant::OBJECT) {
- _set_error("Can't export raw object type.");
- return;
- }
- current_export.type = type;
- current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- // hint expected next!
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- switch (type) {
- case Variant::INT: {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") {
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- _set_error("Expected \",\" in the bit flags hint.");
- return;
- }
-
- current_export.hint = PROPERTY_HINT_FLAGS;
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- bool first = true;
- while (true) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || tokenizer->get_token_constant().get_type() != Variant::STRING) {
- current_export = PropertyInfo();
- _set_error("Expected a string constant in the named bit flags hint.");
- return;
- }
-
- String c = tokenizer->get_token_constant();
- if (!first) {
- current_export.hint_string += ",";
- } else {
- first = false;
- }
-
- current_export.hint_string += c.xml_escape();
-
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- break;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- current_export = PropertyInfo();
- _set_error("Expected \")\" or \",\" in the named bit flags hint.");
- return;
- }
- _ADVANCE_AND_CONSUME_NEWLINES;
- }
-
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_RENDER") {
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the layers 2D render hint.");
- return;
- }
- current_export.hint = PROPERTY_HINT_LAYERS_2D_RENDER;
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_PHYSICS") {
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the layers 2D physics hint.");
- return;
- }
- current_export.hint = PROPERTY_HINT_LAYERS_2D_PHYSICS;
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_RENDER") {
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the layers 3D render hint.");
- return;
- }
- current_export.hint = PROPERTY_HINT_LAYERS_3D_RENDER;
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_PHYSICS") {
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the layers 3D physics hint.");
- return;
- }
- current_export.hint = PROPERTY_HINT_LAYERS_3D_PHYSICS;
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
- //enumeration
- current_export.hint = PROPERTY_HINT_ENUM;
- bool first = true;
- while (true) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || tokenizer->get_token_constant().get_type() != Variant::STRING) {
- current_export = PropertyInfo();
- _set_error("Expected a string constant in the enumeration hint.");
- return;
- }
-
- String c = tokenizer->get_token_constant();
- if (!first) {
- current_export.hint_string += ",";
- } else {
- first = false;
- }
-
- current_export.hint_string += c.xml_escape();
-
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- break;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- current_export = PropertyInfo();
- _set_error("Expected \")\" or \",\" in the enumeration hint.");
- return;
- }
-
- _ADVANCE_AND_CONSUME_NEWLINES;
- }
-
- break;
- }
-
- [[fallthrough]];
- }
- case Variant::FLOAT: {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "EASE") {
- current_export.hint = PROPERTY_HINT_EXP_EASING;
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the hint.");
- return;
- }
- break;
- }
-
- // range
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "EXP") {
- current_export.hint = PROPERTY_HINT_EXP_RANGE;
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- break;
- } else if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- _set_error("Expected \")\" or \",\" in the exponential range hint.");
- return;
- }
- _ADVANCE_AND_CONSUME_NEWLINES;
- } else {
- current_export.hint = PROPERTY_HINT_RANGE;
- }
-
- float sign = 1.0;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_SUB) {
- sign = -1;
- _ADVANCE_AND_CONSUME_NEWLINES;
- }
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) {
- current_export = PropertyInfo();
- _set_error("Expected a range in the numeric hint.");
- return;
- }
-
- current_export.hint_string = rtos(sign * double(tokenizer->get_token_constant()));
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- current_export.hint_string = "0," + current_export.hint_string;
- break;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- current_export = PropertyInfo();
- _set_error("Expected \",\" or \")\" in the numeric range hint.");
- return;
- }
-
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- sign = 1.0;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_SUB) {
- sign = -1;
- _ADVANCE_AND_CONSUME_NEWLINES;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) {
- current_export = PropertyInfo();
- _set_error("Expected a number as upper bound in the numeric range hint.");
- return;
- }
-
- current_export.hint_string += "," + rtos(sign * double(tokenizer->get_token_constant()));
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- break;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- current_export = PropertyInfo();
- _set_error("Expected \",\" or \")\" in the numeric range hint.");
- return;
- }
-
- _ADVANCE_AND_CONSUME_NEWLINES;
- sign = 1.0;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_SUB) {
- sign = -1;
- _ADVANCE_AND_CONSUME_NEWLINES;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) {
- current_export = PropertyInfo();
- _set_error("Expected a number as step in the numeric range hint.");
- return;
- }
-
- current_export.hint_string += "," + rtos(sign * double(tokenizer->get_token_constant()));
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- } break;
- case Variant::STRING: {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type() == Variant::STRING) {
- //enumeration
- current_export.hint = PROPERTY_HINT_ENUM;
- bool first = true;
- while (true) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || tokenizer->get_token_constant().get_type() != Variant::STRING) {
- current_export = PropertyInfo();
- _set_error("Expected a string constant in the enumeration hint.");
- return;
- }
-
- String c = tokenizer->get_token_constant();
- if (!first) {
- current_export.hint_string += ",";
- } else {
- first = false;
- }
-
- current_export.hint_string += c.xml_escape();
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- break;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- current_export = PropertyInfo();
- _set_error("Expected \")\" or \",\" in the enumeration hint.");
- return;
- }
- _ADVANCE_AND_CONSUME_NEWLINES;
- }
-
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "DIR") {
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- current_export.hint = PROPERTY_HINT_DIR;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_IDENTIFIER || !(tokenizer->get_token_identifier() == "GLOBAL")) {
- _set_error("Expected \"GLOBAL\" after comma in the directory hint.");
- return;
- }
- if (!p_class->tool) {
- _set_error("Global filesystem hints may only be used in tool scripts.");
- return;
- }
- current_export.hint = PROPERTY_HINT_GLOBAL_DIR;
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the hint.");
- return;
- }
- } else {
- _set_error("Expected \")\" or \",\" in the hint.");
- return;
- }
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FILE") {
- current_export.hint = PROPERTY_HINT_FILE;
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "GLOBAL") {
- if (!p_class->tool) {
- _set_error("Global filesystem hints may only be used in tool scripts.");
- return;
- }
- current_export.hint = PROPERTY_HINT_GLOBAL_FILE;
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- break;
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- _ADVANCE_AND_CONSUME_NEWLINES;
- } else {
- _set_error("Expected \")\" or \",\" in the hint.");
- return;
- }
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || tokenizer->get_token_constant().get_type() != Variant::STRING) {
- if (current_export.hint == PROPERTY_HINT_GLOBAL_FILE) {
- _set_error("Expected string constant with filter.");
- } else {
- _set_error("Expected \"GLOBAL\" or string constant with filter.");
- }
- return;
- }
- current_export.hint_string = tokenizer->get_token_constant();
- _ADVANCE_AND_CONSUME_NEWLINES;
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the hint.");
- return;
- }
- break;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "MULTILINE") {
- current_export.hint = PROPERTY_HINT_MULTILINE_TEXT;
- _ADVANCE_AND_CONSUME_NEWLINES;
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- _set_error("Expected \")\" in the hint.");
- return;
- }
- break;
- }
- } break;
- case Variant::COLOR: {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_IDENTIFIER) {
- current_export = PropertyInfo();
- _set_error("Color type hint expects RGB or RGBA as hints.");
- return;
- }
-
- String identifier = tokenizer->get_token_identifier();
- if (identifier == "RGB") {
- current_export.hint = PROPERTY_HINT_COLOR_NO_ALPHA;
- } else if (identifier == "RGBA") {
- //none
- } else {
- current_export = PropertyInfo();
- _set_error("Color type hint expects RGB or RGBA as hints.");
- return;
- }
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- } break;
- default: {
- current_export = PropertyInfo();
- _set_error("Type \"" + Variant::get_type_name(type) + "\" can't take hints.");
- return;
- } break;
- }
- }
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "assert".)");
+ assert->condition = parse_expression(false);
+ if (assert->condition == nullptr) {
+ push_error("Expected expression to assert.");
+ return nullptr;
+ }
- } else {
- parenthesis++;
- Node *subexpr = _parse_and_reduce_expression(p_class, true, true);
- if (!subexpr) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
- parenthesis--;
+ if (match(GDScriptTokenizer::Token::COMMA)) {
+ // Error message.
+ if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected error message for assert after ",".)")) {
+ assert->message = parse_literal();
+ if (assert->message->value.get_type() != Variant::STRING) {
+ push_error(R"(Expected string for assert error message.)");
+ }
+ } else {
+ return nullptr;
+ }
+ }
- if (subexpr->type != Node::TYPE_CONSTANT) {
- current_export = PropertyInfo();
- _set_error("Expected a constant expression.");
- }
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*");
- Variant constant = static_cast<ConstantNode *>(subexpr)->value;
+ end_statement(R"("assert")");
- if (constant.get_type() == Variant::OBJECT) {
- GDScriptNativeClass *native_class = Object::cast_to<GDScriptNativeClass>(constant);
+ return assert;
+}
- if (native_class && ClassDB::is_parent_class(native_class->get_name(), "Resource")) {
- current_export.type = Variant::OBJECT;
- current_export.hint = PROPERTY_HINT_RESOURCE_TYPE;
- current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
+GDScriptParser::BreakNode *GDScriptParser::parse_break() {
+ if (!can_break) {
+ push_error(R"(Cannot use "break" outside of a loop.)");
+ }
+ end_statement(R"("break")");
+ return alloc_node<BreakNode>();
+}
- current_export.hint_string = native_class->get_name();
- current_export.class_name = native_class->get_name();
+GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
+ if (!can_continue) {
+ push_error(R"(Cannot use "continue" outside of a loop or pattern matching block.)");
+ }
+ current_suite->has_continue = true;
+ end_statement(R"("continue")");
+ ContinueNode *cont = alloc_node<ContinueNode>();
+ cont->is_for_match = is_continue_match;
+ return cont;
+}
- } else {
- current_export = PropertyInfo();
- _set_error("The export hint isn't a resource type.");
- }
- } else if (constant.get_type() == Variant::DICTIONARY) {
- // Enumeration
- bool is_flags = false;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- _ADVANCE_AND_CONSUME_NEWLINES;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") {
- is_flags = true;
- _ADVANCE_AND_CONSUME_NEWLINES;
- } else {
- current_export = PropertyInfo();
- _set_error("Expected \"FLAGS\" after comma.");
- }
- }
+GDScriptParser::ForNode *GDScriptParser::parse_for() {
+ ForNode *n_for = alloc_node<ForNode>();
- current_export.type = Variant::INT;
- current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
- current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
- Dictionary enum_values = constant;
-
- List<Variant> keys;
- enum_values.get_key_list(&keys);
-
- bool first = true;
- for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- if (enum_values[E->get()].get_type() == Variant::INT) {
- if (!first) {
- current_export.hint_string += ",";
- } else {
- first = false;
- }
-
- current_export.hint_string += E->get().operator String().camelcase_to_underscore(true).capitalize().xml_escape();
- if (!is_flags) {
- current_export.hint_string += ":";
- current_export.hint_string += enum_values[E->get()].operator String().xml_escape();
- }
- }
- }
- } else {
- current_export = PropertyInfo();
- _set_error("Expected type for export.");
- return;
- }
- }
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected loop variable name after "for".)")) {
+ n_for->variable = parse_identifier();
+ }
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
- current_export = PropertyInfo();
- _set_error("Expected \")\" or \",\" after the export hint.");
- return;
- }
+ consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "for" variable name.)");
- tokenizer->advance();
- parenthesis--;
+ n_for->list = parse_expression(false);
- if (is_arrayed) {
- hint_prefix += itos(current_export.type);
- if (current_export.hint) {
- hint_prefix += "/" + itos(current_export.hint);
- }
- current_export.hint_string = hint_prefix + ":" + current_export.hint_string;
- current_export.hint = PROPERTY_HINT_TYPE_STRING;
- current_export.type = Variant::ARRAY;
- }
-#undef _ADVANCE_AND_CONSUME_NEWLINES
- }
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)");
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPET && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPETSYNC) {
- current_export = PropertyInfo();
- _set_error("Expected \"var\", \"onready\", \"remote\", \"master\", \"puppet\", \"remotesync\", \"mastersync\", \"puppetsync\".");
- return;
- }
+ // Save break/continue state.
+ bool could_break = can_break;
+ bool could_continue = can_continue;
+ bool was_continue_match = is_continue_match;
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_ONREADY: {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR) {
- _set_error("Expected \"var\".");
- return;
- }
+ // Allow break/continue.
+ can_break = true;
+ can_continue = true;
+ is_continue_match = false;
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_REMOTE: {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
- if (current_export.type) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR) {
- _set_error("Expected \"var\".");
- return;
- }
+ SuiteNode *suite = alloc_node<SuiteNode>();
+ if (n_for->variable) {
+ suite->add_local(SuiteNode::Local(n_for->variable));
+ }
+ suite->parent_for = n_for;
- } else {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
- _set_error("Expected \"var\" or \"func\".");
- return;
- }
- }
- rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE;
-
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_MASTER: {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
- if (current_export.type) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR) {
- _set_error("Expected \"var\".");
- return;
- }
+ n_for->loop = parse_suite(R"("for" block)", suite);
- } else {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
- _set_error("Expected \"var\" or \"func\".");
- return;
- }
- }
+ // Reset break/continue state.
+ can_break = could_break;
+ can_continue = could_continue;
+ is_continue_match = was_continue_match;
- rpc_mode = MultiplayerAPI::RPC_MODE_MASTER;
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_PUPPET: {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
- if (current_export.type) {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR) {
- _set_error("Expected \"var\".");
- return;
- }
+ return n_for;
+}
- } else {
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
- _set_error("Expected \"var\" or \"func\".");
- return;
- }
- }
+GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
+ IfNode *n_if = alloc_node<IfNode>();
- rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET;
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_REMOTESYNC: {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
- if (current_export.type) {
- _set_error("Expected \"var\".");
- } else {
- _set_error("Expected \"var\" or \"func\".");
- }
- return;
- }
+ n_if->condition = parse_expression(false);
+ if (n_if->condition == nullptr) {
+ push_error(vformat(R"(Expected conditional expression after "%s".)", p_token));
+ }
- rpc_mode = MultiplayerAPI::RPC_MODE_REMOTESYNC;
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_MASTERSYNC: {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
- if (current_export.type) {
- _set_error("Expected \"var\".");
- } else {
- _set_error("Expected \"var\" or \"func\".");
- }
- return;
- }
+ consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":" after "%s" condition.)", p_token));
- rpc_mode = MultiplayerAPI::RPC_MODE_MASTERSYNC;
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_PUPPETSYNC: {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
- if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_FUNCTION) {
- if (current_export.type) {
- _set_error("Expected \"var\".");
- } else {
- _set_error("Expected \"var\" or \"func\".");
- }
- return;
- }
+ n_if->true_block = parse_suite(vformat(R"("%s" block)", p_token));
+ n_if->true_block->parent_if = n_if;
- rpc_mode = MultiplayerAPI::RPC_MODE_PUPPETSYNC;
- continue;
- } break;
- case GDScriptTokenizer::TK_PR_VAR: {
- // variable declaration and (eventual) initialization
+ if (n_if->true_block->has_continue) {
+ current_suite->has_continue = true;
+ }
- ClassNode::Member member;
+ if (match(GDScriptTokenizer::Token::ELIF)) {
+ IfNode *elif = parse_if("elif");
- bool autoexport = tokenizer->get_token(-1) == GDScriptTokenizer::TK_PR_EXPORT;
- if (current_export.type != Variant::NIL) {
- member._export = current_export;
- current_export = PropertyInfo();
- }
+ SuiteNode *else_block = alloc_node<SuiteNode>();
+ else_block->statements.push_back(elif);
+ n_if->false_block = else_block;
+ } else if (match(GDScriptTokenizer::Token::ELSE)) {
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)");
+ n_if->false_block = parse_suite(R"("else" block)");
+ }
- bool onready = tokenizer->get_token(-1) == GDScriptTokenizer::TK_PR_ONREADY;
+ if (n_if->false_block != nullptr && n_if->false_block->has_return && n_if->true_block->has_return) {
+ current_suite->has_return = true;
+ }
+ if (n_if->false_block != nullptr && n_if->false_block->has_continue) {
+ current_suite->has_continue = true;
+ }
- tokenizer->advance();
- if (!tokenizer->is_token_literal(0, true)) {
- _set_error("Expected an identifier for the member variable name.");
- return;
- }
+ return n_if;
+}
- member.identifier = tokenizer->get_token_literal();
- member.expression = nullptr;
- member._export.name = member.identifier;
- member.line = tokenizer->get_token_line();
- member.usages = 0;
- member.rpc_mode = rpc_mode;
-
- if (current_class->constant_expressions.has(member.identifier)) {
- _set_error("A constant named \"" + String(member.identifier) + "\" already exists in this class (at line: " +
- itos(current_class->constant_expressions[member.identifier].expression->line) + ").");
- return;
- }
+GDScriptParser::MatchNode *GDScriptParser::parse_match() {
+ MatchNode *match = alloc_node<MatchNode>();
- for (int i = 0; i < current_class->variables.size(); i++) {
- if (current_class->variables[i].identifier == member.identifier) {
- _set_error("Variable \"" + String(member.identifier) + "\" already exists in this class (at line: " +
- itos(current_class->variables[i].line) + ").");
- return;
- }
- }
+ match->test = parse_expression(false);
+ if (match->test == nullptr) {
+ push_error(R"(Expected expression to test after "match".)");
+ }
- for (int i = 0; i < current_class->subclasses.size(); i++) {
- if (current_class->subclasses[i]->name == member.identifier) {
- _set_error("A class named \"" + String(member.identifier) + "\" already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ").");
- return;
- }
- }
-#ifdef DEBUG_ENABLED
- for (int i = 0; i < current_class->functions.size(); i++) {
- if (current_class->functions[i]->name == member.identifier) {
- _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier);
- break;
- }
- }
- for (int i = 0; i < current_class->static_functions.size(); i++) {
- if (current_class->static_functions[i]->name == member.identifier) {
- _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier);
- break;
- }
- }
-#endif // DEBUG_ENABLED
- tokenizer->advance();
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" expression.)");
+ consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)");
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) {
+ return match;
+ }
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
- if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
- member.data_type = DataType();
#ifdef DEBUG_ENABLED
- member.data_type.infer_type = true;
+ bool all_have_return = true;
+ bool have_wildcard = false;
+ bool wildcard_has_return = false;
+ bool have_wildcard_without_continue = false;
#endif
- tokenizer->advance();
- } else if (!_parse_type(member.data_type)) {
- _set_error("Expected a type for the class variable.");
- return;
- }
- }
-
- if (autoexport && member.data_type.has_type) {
- if (member.data_type.kind == DataType::BUILTIN) {
- member._export.type = member.data_type.builtin_type;
- } else if (member.data_type.kind == DataType::NATIVE) {
- if (ClassDB::is_parent_class(member.data_type.native_type, "Resource")) {
- member._export.type = Variant::OBJECT;
- member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
- member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
- member._export.hint_string = member.data_type.native_type;
- member._export.class_name = member.data_type.native_type;
- } else {
- _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
- return;
- }
-
- } else {
- _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
- return;
- }
- }
-#ifdef TOOLS_ENABLED
- Callable::CallError ce;
- member.default_value = Variant::construct(member._export.type, nullptr, 0, ce);
-#endif
+ while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
+ MatchBranchNode *branch = parse_match_branch();
+ if (branch == nullptr) {
+ continue;
+ }
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
#ifdef DEBUG_ENABLED
- int line = tokenizer->get_token_line();
-#endif
- tokenizer->advance();
-
- Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport || member._export.type != Variant::NIL);
- if (!subexpr) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- //discourage common error
- if (!onready && subexpr->type == Node::TYPE_OPERATOR) {
- OperatorNode *op = static_cast<OperatorNode *>(subexpr);
- if (op->op == OperatorNode::OP_CALL && op->arguments[0]->type == Node::TYPE_SELF && op->arguments[1]->type == Node::TYPE_IDENTIFIER) {
- IdentifierNode *id = static_cast<IdentifierNode *>(op->arguments[1]);
- if (id->name == "get_node") {
- _set_error("Use \"onready var " + String(member.identifier) + " = get_node(...)\" instead.");
- return;
- }
- }
- }
-
- member.expression = subexpr;
-
- if (autoexport && !member.data_type.has_type) {
- if (subexpr->type != Node::TYPE_CONSTANT) {
- _set_error("Type-less export needs a constant expression assigned to infer type.");
- return;
- }
-
- ConstantNode *cn = static_cast<ConstantNode *>(subexpr);
- if (cn->value.get_type() == Variant::NIL) {
- _set_error("Can't accept a null constant expression for inferring export type.");
- return;
- }
-
- if (!_reduce_export_var_type(cn->value, member.line)) {
- return;
- }
+ if (have_wildcard_without_continue) {
+ push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN);
+ }
- member._export.type = cn->value.get_type();
- member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
- if (cn->value.get_type() == Variant::OBJECT) {
- Object *obj = cn->value;
- Resource *res = Object::cast_to<Resource>(obj);
- if (res == nullptr) {
- _set_error("The exported constant isn't a type or resource.");
- return;
- }
- member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
- member._export.hint_string = res->get_class();
- }
- }
-#ifdef TOOLS_ENABLED
- if (subexpr->type == Node::TYPE_CONSTANT && (member._export.type != Variant::NIL || member.data_type.has_type)) {
- ConstantNode *cn = static_cast<ConstantNode *>(subexpr);
- if (cn->value.get_type() != Variant::NIL) {
- if (member._export.type != Variant::NIL && cn->value.get_type() != member._export.type) {
- if (Variant::can_convert(cn->value.get_type(), member._export.type)) {
- Callable::CallError err;
- const Variant *args = &cn->value;
- cn->value = Variant::construct(member._export.type, &args, 1, err);
- } else {
- _set_error("Can't convert the provided value to the export type.");
- return;
- }
- }
- member.default_value = cn->value;
- }
- }
+ if (branch->has_wildcard) {
+ have_wildcard = true;
+ if (branch->block->has_return) {
+ wildcard_has_return = true;
+ }
+ if (!branch->block->has_continue) {
+ have_wildcard_without_continue = true;
+ }
+ }
+ if (!branch->block->has_return) {
+ all_have_return = false;
+ }
#endif
+ match->branches.push_back(branch);
+ }
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = member.identifier;
- id->datatype = member.data_type;
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_INIT_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(subexpr);
+ consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)");
#ifdef DEBUG_ENABLED
- NewLineNode *nl2 = alloc_node<NewLineNode>();
- nl2->line = line;
- if (onready) {
- p_class->ready->statements.push_back(nl2);
- } else {
- p_class->initializer->statements.push_back(nl2);
- }
+ if (wildcard_has_return || (all_have_return && have_wildcard)) {
+ current_suite->has_return = true;
+ }
#endif
- if (onready) {
- p_class->ready->statements.push_back(op);
- } else {
- p_class->initializer->statements.push_back(op);
- }
-
- member.initial_assignment = op;
- } else {
- if (autoexport && !member.data_type.has_type) {
- _set_error("Type-less export needs a constant expression assigned to infer type.");
- return;
- }
-
- Node *expr;
-
- if (member.data_type.has_type) {
- expr = _get_default_value_for_type(member.data_type);
- } else {
- DataType exported_type;
- exported_type.has_type = true;
- exported_type.kind = DataType::BUILTIN;
- exported_type.builtin_type = member._export.type;
- expr = _get_default_value_for_type(exported_type);
- }
-
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = member.identifier;
- id->datatype = member.data_type;
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_INIT_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(expr);
-
- p_class->initializer->statements.push_back(op);
-
- member.initial_assignment = op;
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_SETGET) {
- tokenizer->advance();
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_COMMA) {
- //just comma means using only getter
- if (!tokenizer->is_token_literal()) {
- _set_error("Expected an identifier for the setter function after \"setget\".");
- }
-
- member.setter = tokenizer->get_token_literal();
-
- tokenizer->advance();
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- //there is a getter
- tokenizer->advance();
-
- if (!tokenizer->is_token_literal()) {
- _set_error("Expected an identifier for the getter function after \",\".");
- }
-
- member.getter = tokenizer->get_token_literal();
- tokenizer->advance();
- }
- }
-
- p_class->variables.push_back(member);
-
- if (!_end_statement()) {
- _set_end_statement_error("var");
- return;
- }
- } break;
- case GDScriptTokenizer::TK_PR_CONST: {
- // constant declaration and initialization
-
- ClassNode::Constant constant;
-
- tokenizer->advance();
- if (!tokenizer->is_token_literal(0, true)) {
- _set_error("Expected an identifier for the constant.");
- return;
- }
-
- StringName const_id = tokenizer->get_token_literal();
- int line = tokenizer->get_token_line();
-
- if (current_class->constant_expressions.has(const_id)) {
- _set_error("Constant \"" + String(const_id) + "\" already exists in this class (at line " +
- itos(current_class->constant_expressions[const_id].expression->line) + ").");
- return;
- }
-
- for (int i = 0; i < current_class->variables.size(); i++) {
- if (current_class->variables[i].identifier == const_id) {
- _set_error("A variable named \"" + String(const_id) + "\" already exists in this class (at line " +
- itos(current_class->variables[i].line) + ").");
- return;
- }
- }
-
- for (int i = 0; i < current_class->subclasses.size(); i++) {
- if (current_class->subclasses[i]->name == const_id) {
- _set_error("A class named \"" + String(const_id) + "\" already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ").");
- return;
- }
- }
-
- tokenizer->advance();
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) {
- if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) {
- constant.type = DataType();
-#ifdef DEBUG_ENABLED
- constant.type.infer_type = true;
-#endif
- tokenizer->advance();
- } else if (!_parse_type(constant.type)) {
- _set_error("Expected a type for the class constant.");
- return;
- }
- }
-
- if (tokenizer->get_token() != GDScriptTokenizer::TK_OP_ASSIGN) {
- _set_error("Constants must be assigned immediately.");
- return;
- }
-
- tokenizer->advance();
-
- Node *subexpr = _parse_and_reduce_expression(p_class, true, true);
- if (!subexpr) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- if (subexpr->type != Node::TYPE_CONSTANT) {
- _set_error("Expected a constant expression.", line);
- return;
- }
- subexpr->line = line;
- constant.expression = subexpr;
-
- p_class->constant_expressions.insert(const_id, constant);
-
- if (!_end_statement()) {
- _set_end_statement_error("const");
- return;
- }
-
- } break;
- case GDScriptTokenizer::TK_PR_ENUM: {
- //multiple constant declarations..
-
- int last_assign = -1; // Incremented by 1 right before the assignment.
- String enum_name;
- Dictionary enum_dict;
-
- tokenizer->advance();
- if (tokenizer->is_token_literal(0, true)) {
- enum_name = tokenizer->get_token_literal();
-
- if (current_class->constant_expressions.has(enum_name)) {
- _set_error("A constant named \"" + String(enum_name) + "\" already exists in this class (at line " +
- itos(current_class->constant_expressions[enum_name].expression->line) + ").");
- return;
- }
-
- for (int i = 0; i < current_class->variables.size(); i++) {
- if (current_class->variables[i].identifier == enum_name) {
- _set_error("A variable named \"" + String(enum_name) + "\" already exists in this class (at line " +
- itos(current_class->variables[i].line) + ").");
- return;
- }
- }
-
- for (int i = 0; i < current_class->subclasses.size(); i++) {
- if (current_class->subclasses[i]->name == enum_name) {
- _set_error("A class named \"" + String(enum_name) + "\" already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ").");
- return;
- }
- }
-
- tokenizer->advance();
- }
- if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) {
- _set_error("Expected \"{\" in the enum declaration.");
- return;
- }
- tokenizer->advance();
-
- while (true) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) {
- tokenizer->advance(); // Ignore newlines
- } else if (tokenizer->get_token() == GDScriptTokenizer::TK_CURLY_BRACKET_CLOSE) {
- tokenizer->advance();
- break; // End of enum
- } else if (!tokenizer->is_token_literal(0, true)) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_EOF) {
- _set_error("Unexpected end of file.");
- } else {
- _set_error(String("Unexpected ") + GDScriptTokenizer::get_token_name(tokenizer->get_token()) + ", expected an identifier.");
- }
-
- return;
- } else { // tokenizer->is_token_literal(0, true)
- StringName const_id = tokenizer->get_token_literal();
-
- tokenizer->advance();
-
- ConstantNode *enum_value_expr;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
- tokenizer->advance();
-
- Node *subexpr = _parse_and_reduce_expression(p_class, true, true);
- if (!subexpr) {
- if (_recover_from_completion()) {
- break;
- }
- return;
- }
-
- if (subexpr->type != Node::TYPE_CONSTANT) {
- _set_error("Expected a constant expression.");
- return;
- }
-
- enum_value_expr = static_cast<ConstantNode *>(subexpr);
-
- if (enum_value_expr->value.get_type() != Variant::INT) {
- _set_error("Expected an integer value for \"enum\".");
- return;
- }
-
- last_assign = enum_value_expr->value;
-
- } else {
- last_assign = last_assign + 1;
- enum_value_expr = alloc_node<ConstantNode>();
- enum_value_expr->value = last_assign;
- enum_value_expr->datatype = _type_from_variant(enum_value_expr->value);
- }
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- } else if (tokenizer->is_token_literal(0, true)) {
- _set_error("Unexpected identifier.");
- return;
- }
-
- if (enum_name != "") {
- enum_dict[const_id] = enum_value_expr->value;
- } else {
- if (current_class->constant_expressions.has(const_id)) {
- _set_error("A constant named \"" + String(const_id) + "\" already exists in this class (at line " +
- itos(current_class->constant_expressions[const_id].expression->line) + ").");
- return;
- }
-
- for (int i = 0; i < current_class->variables.size(); i++) {
- if (current_class->variables[i].identifier == const_id) {
- _set_error("A variable named \"" + String(const_id) + "\" already exists in this class (at line " +
- itos(current_class->variables[i].line) + ").");
- return;
- }
- }
-
- for (int i = 0; i < current_class->subclasses.size(); i++) {
- if (current_class->subclasses[i]->name == const_id) {
- _set_error("A class named \"" + String(const_id) + "\" already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ").");
- return;
- }
- }
-
- ClassNode::Constant constant;
- constant.type.has_type = true;
- constant.type.kind = DataType::BUILTIN;
- constant.type.builtin_type = Variant::INT;
- constant.expression = enum_value_expr;
- p_class->constant_expressions.insert(const_id, constant);
- }
- }
- }
+ return match;
+}
- if (enum_name != "") {
- ClassNode::Constant enum_constant;
- ConstantNode *cn = alloc_node<ConstantNode>();
- cn->value = enum_dict;
- cn->datatype = _type_from_variant(cn->value);
+GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
+ MatchBranchNode *branch = alloc_node<MatchBranchNode>();
- enum_constant.expression = cn;
- enum_constant.type = cn->datatype;
- p_class->constant_expressions.insert(enum_name, enum_constant);
- }
+ bool has_bind = false;
- if (!_end_statement()) {
- _set_end_statement_error("enum");
- return;
- }
+ do {
+ PatternNode *pattern = parse_match_pattern();
+ if (pattern == nullptr) {
+ continue;
+ }
+ if (pattern->pattern_type == PatternNode::PT_BIND) {
+ has_bind = true;
+ }
+ if (branch->patterns.size() > 0 && has_bind) {
+ push_error(R"(Cannot use a variable bind with multiple patterns.)");
+ }
+ if (pattern->pattern_type == PatternNode::PT_REST) {
+ push_error(R"(Rest pattern can only be used inside array and dictionary patterns.)");
+ } else if (pattern->pattern_type == PatternNode::PT_BIND || pattern->pattern_type == PatternNode::PT_WILDCARD) {
+ branch->has_wildcard = true;
+ }
+ branch->patterns.push_back(pattern);
+ } while (match(GDScriptTokenizer::Token::COMMA));
- } break;
+ if (branch->patterns.empty()) {
+ push_error(R"(No pattern found for "match" branch.)");
+ }
- case GDScriptTokenizer::TK_CONSTANT: {
- if (tokenizer->get_token_constant().get_type() == Variant::STRING) {
- tokenizer->advance();
- // Ignore
- } else {
- _set_error(String() + "Unexpected constant of type: " + Variant::get_type_name(tokenizer->get_token_constant().get_type()));
- return;
- }
- } break;
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)");
- case GDScriptTokenizer::TK_CF_PASS: {
- tokenizer->advance();
- } break;
+ // Save continue state.
+ bool could_continue = can_continue;
+ bool was_continue_match = is_continue_match;
+ // Allow continue for match.
+ can_continue = true;
+ is_continue_match = true;
- default: {
- _set_error(String() + "Unexpected token: " + tokenizer->get_token_name(tokenizer->get_token()) + ":" + tokenizer->get_token_identifier());
- return;
+ SuiteNode *suite = alloc_node<SuiteNode>();
+ if (branch->patterns.size() > 0) {
+ List<StringName> binds;
+ branch->patterns[0]->binds.get_key_list(&binds);
- } break;
+ for (List<StringName>::Element *E = binds.front(); E != nullptr; E = E->next()) {
+ SuiteNode::Local local(branch->patterns[0]->binds[E->get()]);
+ suite->add_local(local);
}
}
-}
-void GDScriptParser::_determine_inheritance(ClassNode *p_class, bool p_recursive) {
- if (p_class->base_type.has_type) {
- // Already determined
- } else if (p_class->extends_used) {
- //do inheritance
- String path = p_class->extends_file;
+ branch->block = parse_suite("match pattern block", suite);
- Ref<GDScript> script;
- StringName native;
- ClassNode *base_class = nullptr;
+ // Restore continue state.
+ can_continue = could_continue;
+ is_continue_match = was_continue_match;
- if (path != "") {
- //path (and optionally subclasses)
+ return branch;
+}
- if (path.is_rel_path()) {
- String base = base_path;
+GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) {
+ PatternNode *pattern = alloc_node<PatternNode>();
- if (base == "" || base.is_rel_path()) {
- _set_error("Couldn't resolve relative path for the parent class: " + path, p_class->line);
- return;
- }
- path = base.plus_file(path).simplify_path();
- }
- script = ResourceLoader::load(path);
- if (script.is_null()) {
- _set_error("Couldn't load the base class: " + path, p_class->line);
- return;
+ switch (current.type) {
+ case GDScriptTokenizer::Token::LITERAL:
+ advance();
+ pattern->pattern_type = PatternNode::PT_LITERAL;
+ pattern->literal = parse_literal();
+ if (pattern->literal == nullptr) {
+ // Error happened.
+ return nullptr;
}
- if (!script->is_valid()) {
- _set_error("Script isn't fully loaded (cyclic preload?): " + path, p_class->line);
- return;
+ break;
+ case GDScriptTokenizer::Token::VAR: {
+ // Bind.
+ advance();
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) {
+ return nullptr;
}
+ pattern->pattern_type = PatternNode::PT_BIND;
+ pattern->bind = parse_identifier();
- if (p_class->extends_class.size()) {
- for (int i = 0; i < p_class->extends_class.size(); i++) {
- String sub = p_class->extends_class[i];
- if (script->get_subclasses().has(sub)) {
- Ref<Script> subclass = script->get_subclasses()[sub]; //avoid reference from disappearing
- script = subclass;
- } else {
- _set_error("Couldn't find the subclass: " + sub, p_class->line);
- return;
- }
+ PatternNode *root_pattern = p_root_pattern == nullptr ? pattern : p_root_pattern;
+
+ if (p_root_pattern != nullptr) {
+ if (p_root_pattern->has_bind(pattern->bind->name)) {
+ push_error(vformat(R"(Bind variable name "%s" was already used in this pattern.)", pattern->bind->name));
+ return nullptr;
}
}
- } else {
- if (p_class->extends_class.size() == 0) {
- _set_error("Parser bug: undecidable inheritance.", p_class->line);
- ERR_FAIL();
+ if (current_suite->has_local(pattern->bind->name)) {
+ push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name));
+ return nullptr;
}
- //look around for the subclasses
-
- int extend_iter = 1;
- String base = p_class->extends_class[0];
- ClassNode *p = p_class->owner;
- Ref<GDScript> base_script;
-
- if (ScriptServer::is_global_class(base)) {
- base_script = ResourceLoader::load(ScriptServer::get_global_class_path(base));
- if (!base_script.is_valid()) {
- _set_error("The class \"" + base + "\" couldn't be fully loaded (script error or cyclic dependency).", p_class->line);
- return;
- }
- p = nullptr;
- } else {
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
+
+ root_pattern->binds[pattern->bind->name] = pattern->bind;
+
+ } break;
+ case GDScriptTokenizer::Token::UNDERSCORE:
+ // Wildcard.
+ advance();
+ pattern->pattern_type = PatternNode::PT_WILDCARD;
+ break;
+ case GDScriptTokenizer::Token::PERIOD_PERIOD:
+ // Rest.
+ advance();
+ pattern->pattern_type = PatternNode::PT_REST;
+ break;
+ case GDScriptTokenizer::Token::BRACKET_OPEN: {
+ // Array.
+ advance();
+ pattern->pattern_type = PatternNode::PT_ARRAY;
+
+ if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
+ do {
+ PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
+ if (sub_pattern == nullptr) {
continue;
}
- String name = s.get_slice("/", 1);
- if (name == base) {
- String singleton_path = ProjectSettings::get_singleton()->get(s);
- if (singleton_path.begins_with("*")) {
- singleton_path = singleton_path.right(1);
- }
- if (!singleton_path.begins_with("res://")) {
- singleton_path = "res://" + singleton_path;
- }
- base_script = ResourceLoader::load(singleton_path);
- if (!base_script.is_valid()) {
- _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic inheritance).", p_class->line);
- return;
- }
- p = nullptr;
+ if (pattern->rest_used) {
+ push_error(R"(The ".." pattern must be the last element in the pattern array.)");
+ } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
+ pattern->rest_used = true;
}
- }
+ pattern->array.push_back(sub_pattern);
+ } while (match(GDScriptTokenizer::Token::COMMA));
}
-
- while (p) {
- bool found = false;
-
- for (int i = 0; i < p->subclasses.size(); i++) {
- if (p->subclasses[i]->name == base) {
- ClassNode *test = p->subclasses[i];
- while (test) {
- if (test == p_class) {
- _set_error("Cyclic inheritance.", test->line);
- return;
+ consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" to close the array pattern.)");
+ break;
+ }
+ case GDScriptTokenizer::Token::BRACE_OPEN: {
+ // Dictionary.
+ advance();
+ pattern->pattern_type = PatternNode::PT_DICTIONARY;
+
+ if (!check(GDScriptTokenizer::Token::BRACE_CLOSE) && !is_at_end()) {
+ do {
+ if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) {
+ // Rest.
+ if (pattern->rest_used) {
+ push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
+ } else {
+ PatternNode *sub_pattern = alloc_node<PatternNode>();
+ sub_pattern->pattern_type = PatternNode::PT_REST;
+ pattern->dictionary.push_back({ nullptr, sub_pattern });
+ pattern->rest_used = true;
+ }
+ } else {
+ ExpressionNode *key = parse_expression(false);
+ if (key == nullptr) {
+ push_error(R"(Expected expression as key for dictionary pattern.)");
+ }
+ if (match(GDScriptTokenizer::Token::COLON)) {
+ // Value pattern.
+ PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
+ if (sub_pattern == nullptr) {
+ continue;
}
- if (test->base_type.kind == DataType::CLASS) {
- test = test->base_type.class_type;
+ if (pattern->rest_used) {
+ push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
+ } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
+ push_error(R"(The ".." pattern cannot be used as a value.)");
} else {
- break;
+ pattern->dictionary.push_back({ key, sub_pattern });
}
- }
- found = true;
- if (extend_iter < p_class->extends_class.size()) {
- // Keep looking at current classes if possible
- base = p_class->extends_class[extend_iter++];
- p = p->subclasses[i];
} else {
- base_class = p->subclasses[i];
+ // Key match only.
+ pattern->dictionary.push_back({ key, nullptr });
}
- break;
- }
- }
-
- if (base_class) {
- break;
- }
- if (found) {
- continue;
- }
-
- if (p->constant_expressions.has(base)) {
- if (p->constant_expressions[base].expression->type != Node::TYPE_CONSTANT) {
- _set_error("Couldn't resolve the constant \"" + base + "\".", p_class->line);
- return;
- }
- const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[base].expression);
- base_script = cn->value;
- if (base_script.is_null()) {
- _set_error("Constant isn't a class: " + base, p_class->line);
- return;
}
- break;
- }
-
- p = p->owner;
+ } while (match(GDScriptTokenizer::Token::COMMA));
}
-
- if (base_script.is_valid()) {
- String ident = base;
- Ref<GDScript> find_subclass = base_script;
-
- for (int i = extend_iter; i < p_class->extends_class.size(); i++) {
- String subclass = p_class->extends_class[i];
-
- ident += ("." + subclass);
-
- if (find_subclass->get_subclasses().has(subclass)) {
- find_subclass = find_subclass->get_subclasses()[subclass];
- } else if (find_subclass->get_constants().has(subclass)) {
- Ref<GDScript> new_base_class = find_subclass->get_constants()[subclass];
- if (new_base_class.is_null()) {
- _set_error("Constant isn't a class: " + ident, p_class->line);
- return;
- }
- find_subclass = new_base_class;
- } else {
- _set_error("Couldn't find the subclass: " + ident, p_class->line);
- return;
- }
- }
-
- script = find_subclass;
-
- } else if (!base_class) {
- if (p_class->extends_class.size() > 1) {
- _set_error("Invalid inheritance (unknown class + subclasses).", p_class->line);
- return;
- }
- //if not found, try engine classes
- if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
- _set_error("Unknown class: \"" + base + "\"", p_class->line);
- return;
- }
-
- native = base;
- }
- }
-
- if (base_class) {
- p_class->base_type.has_type = true;
- p_class->base_type.kind = DataType::CLASS;
- p_class->base_type.class_type = base_class;
- } else if (script.is_valid()) {
- p_class->base_type.has_type = true;
- p_class->base_type.kind = DataType::GDSCRIPT;
- p_class->base_type.script_type = script;
- p_class->base_type.native_type = script->get_instance_base_type();
- } else if (native != StringName()) {
- p_class->base_type.has_type = true;
- p_class->base_type.kind = DataType::NATIVE;
- p_class->base_type.native_type = native;
- } else {
- _set_error("Couldn't determine inheritance.", p_class->line);
- return;
- }
-
- } else {
- // without extends, implicitly extend Reference
- p_class->base_type.has_type = true;
- p_class->base_type.kind = DataType::NATIVE;
- p_class->base_type.native_type = "Reference";
- }
-
- if (p_recursive) {
- // Recursively determine subclasses
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- _determine_inheritance(p_class->subclasses[i], p_recursive);
+ consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)");
+ break;
}
- }
-}
-
-String GDScriptParser::DataType::to_string() const {
- if (!has_type) {
- return "var";
- }
- switch (kind) {
- case BUILTIN: {
- if (builtin_type == Variant::NIL) {
- return "null";
- }
- return Variant::get_type_name(builtin_type);
- } break;
- case NATIVE: {
- if (is_meta_type) {
- return "GDScriptNativeClass";
- }
- return native_type.operator String();
- } break;
-
- case GDSCRIPT: {
- Ref<GDScript> gds = script_type;
- const String &gds_class = gds->get_script_class_name();
- if (!gds_class.empty()) {
- return gds_class;
+ default: {
+ // Expression.
+ ExpressionNode *expression = parse_expression(false);
+ if (expression == nullptr) {
+ push_error(R"(Expected expression for match pattern.)");
+ } else {
+ pattern->pattern_type = PatternNode::PT_EXPRESSION;
+ pattern->expression = expression;
}
- [[fallthrough]];
+ break;
}
- case SCRIPT: {
- if (is_meta_type) {
- return script_type->get_class_name().operator String();
- }
- String name = script_type->get_name();
- if (name != String()) {
- return name;
- }
- name = script_type->get_path().get_file();
- if (name != String()) {
- return name;
- }
- return native_type.operator String();
- } break;
- case CLASS: {
- ERR_FAIL_COND_V(!class_type, String());
- if (is_meta_type) {
- return "GDScript";
- }
- if (class_type->name == StringName()) {
- return "self";
- }
- return class_type->name.operator String();
- } break;
- case UNRESOLVED: {
- } break;
}
- return "Unresolved";
+ return pattern;
}
-bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) {
- tokenizer->advance();
- r_type.has_type = true;
-
- bool finished = false;
- bool can_index = false;
- String full_name;
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- completion_cursor = StringName();
- completion_type = COMPLETION_TYPE_HINT;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_argument = 0;
- completion_block = current_block;
- completion_found = true;
- completion_ident_is_call = p_can_be_void;
- tokenizer->advance();
- }
+bool GDScriptParser::PatternNode::has_bind(const StringName &p_name) {
+ return binds.has(p_name);
+}
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_PR_VOID: {
- if (!p_can_be_void) {
- return false;
- }
- r_type.kind = DataType::BUILTIN;
- r_type.builtin_type = Variant::NIL;
- } break;
- case GDScriptTokenizer::TK_BUILT_IN_TYPE: {
- r_type.builtin_type = tokenizer->get_token_type();
- if (tokenizer->get_token_type() == Variant::OBJECT) {
- r_type.kind = DataType::NATIVE;
- r_type.native_type = "Object";
- } else {
- r_type.kind = DataType::BUILTIN;
- }
- } break;
- case GDScriptTokenizer::TK_IDENTIFIER: {
- r_type.native_type = tokenizer->get_token_identifier();
- if (ClassDB::class_exists(r_type.native_type) || ClassDB::class_exists("_" + r_type.native_type.operator String())) {
- r_type.kind = DataType::NATIVE;
- } else {
- r_type.kind = DataType::UNRESOLVED;
- can_index = true;
- full_name = r_type.native_type;
- }
- } break;
- default: {
- return false;
- }
- }
+GDScriptParser::IdentifierNode *GDScriptParser::PatternNode::get_bind(const StringName &p_name) {
+ return binds[p_name];
+}
- tokenizer->advance();
+GDScriptParser::WhileNode *GDScriptParser::parse_while() {
+ WhileNode *n_while = alloc_node<WhileNode>();
- if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) {
- completion_cursor = r_type.native_type;
- completion_type = COMPLETION_TYPE_HINT;
- completion_class = current_class;
- completion_function = current_function;
- completion_line = tokenizer->get_token_line();
- completion_argument = 0;
- completion_block = current_block;
- completion_found = true;
- completion_ident_is_call = p_can_be_void;
- tokenizer->advance();
+ n_while->condition = parse_expression(false);
+ if (n_while->condition == nullptr) {
+ push_error(R"(Expected conditional expression after "while".)");
}
- if (can_index) {
- while (!finished) {
- switch (tokenizer->get_token()) {
- case GDScriptTokenizer::TK_PERIOD: {
- if (!can_index) {
- _set_error("Unexpected \".\".");
- return false;
- }
- can_index = false;
- tokenizer->advance();
- } break;
- case GDScriptTokenizer::TK_IDENTIFIER: {
- if (can_index) {
- _set_error("Unexpected identifier.");
- return false;
- }
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "while" condition.)");
- StringName id;
- bool has_completion = _get_completable_identifier(COMPLETION_TYPE_HINT_INDEX, id);
- if (id == StringName()) {
- id = "@temp";
- }
+ // Save break/continue state.
+ bool could_break = can_break;
+ bool could_continue = can_continue;
+ bool was_continue_match = is_continue_match;
- full_name += "." + id.operator String();
- can_index = true;
- if (has_completion) {
- completion_cursor = full_name;
- }
- } break;
- default: {
- finished = true;
- } break;
- }
- }
+ // Allow break/continue.
+ can_break = true;
+ can_continue = true;
+ is_continue_match = false;
- if (tokenizer->get_token(-1) == GDScriptTokenizer::TK_PERIOD) {
- _set_error("Expected a subclass identifier.");
- return false;
- }
+ n_while->loop = parse_suite(R"("while" block)");
- r_type.native_type = full_name;
- }
+ // Reset break/continue state.
+ can_break = could_break;
+ can_continue = could_continue;
+ is_continue_match = was_continue_match;
- return true;
+ return n_while;
}
-GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source, int p_line) {
- if (!p_source.has_type) {
- return p_source;
- }
- if (p_source.kind != DataType::UNRESOLVED) {
- return p_source;
+GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign) {
+ // Switch multiline mode on for grouping tokens.
+ // Do this early to avoid the tokenizer generating whitespace tokens.
+ switch (current.type) {
+ case GDScriptTokenizer::Token::PARENTHESIS_OPEN:
+ case GDScriptTokenizer::Token::BRACE_OPEN:
+ case GDScriptTokenizer::Token::BRACKET_OPEN:
+ push_multiline(true);
+ break;
+ default:
+ break; // Nothing to do.
}
- Vector<String> full_name = p_source.native_type.operator String().split(".", false);
- int name_part = 0;
+ // Completion can appear whenever an expression is expected.
+ make_completion_context(COMPLETION_IDENTIFIER, nullptr);
- DataType result;
- result.has_type = true;
+ GDScriptTokenizer::Token token = advance();
+ ParseFunction prefix_rule = get_rule(token.type)->prefix;
- while (name_part < full_name.size()) {
- bool found = false;
- StringName id = full_name[name_part];
- DataType base_type = result;
+ if (prefix_rule == nullptr) {
+ // Expected expression. Let the caller give the proper error message.
+ return nullptr;
+ }
- ClassNode *p = nullptr;
- if (name_part == 0) {
- if (ScriptServer::is_global_class(id)) {
- String script_path = ScriptServer::get_global_class_path(id);
- if (script_path == self_path) {
- result.kind = DataType::CLASS;
- result.class_type = static_cast<ClassNode *>(head);
- } else {
- Ref<Script> script = ResourceLoader::load(script_path);
- Ref<GDScript> gds = script;
- if (gds.is_valid()) {
- if (!gds->is_valid()) {
- _set_error("The class \"" + id + "\" couldn't be fully loaded (script error or cyclic dependency).", p_line);
- return DataType();
- }
- result.kind = DataType::GDSCRIPT;
- result.script_type = gds;
- } else if (script.is_valid()) {
- result.kind = DataType::SCRIPT;
- result.script_type = script;
- } else {
- _set_error("The class \"" + id + "\" was found in global scope, but its script couldn't be loaded.", p_line);
- return DataType();
- }
- }
- name_part++;
- continue;
- }
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
- String singleton_path;
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
- }
- String name = s.get_slice("/", 1);
- if (name == id) {
- singleton_path = ProjectSettings::get_singleton()->get(s);
- if (singleton_path.begins_with("*")) {
- singleton_path = singleton_path.right(1);
- }
- if (!singleton_path.begins_with("res://")) {
- singleton_path = "res://" + singleton_path;
- }
- break;
- }
- }
- if (!singleton_path.empty()) {
- Ref<Script> script = ResourceLoader::load(singleton_path);
- Ref<GDScript> gds = script;
- if (gds.is_valid()) {
- if (!gds->is_valid()) {
- _set_error("Class '" + id + "' could not be fully loaded (script error or cyclic inheritance).", p_line);
- return DataType();
- }
- result.kind = DataType::GDSCRIPT;
- result.script_type = gds;
- } else if (script.is_valid()) {
- result.kind = DataType::SCRIPT;
- result.script_type = script;
- } else {
- _set_error("Couldn't fully load singleton script '" + id + "' (possible cyclic reference or parse error).", p_line);
- return DataType();
- }
- name_part++;
- continue;
- }
+ ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
- p = current_class;
- } else if (base_type.kind == DataType::CLASS) {
- p = base_type.class_type;
+ while (p_precedence <= get_rule(current.type)->precedence) {
+ if (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) {
+ return previous_operand;
}
- while (p) {
- if (p->constant_expressions.has(id)) {
- if (p->constant_expressions[id].expression->type != Node::TYPE_CONSTANT) {
- _set_error("Parser bug: unresolved constant.", p_line);
- ERR_FAIL_V(result);
- }
- const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[id].expression);
- Ref<GDScript> gds = cn->value;
- if (gds.is_valid()) {
- result.kind = DataType::GDSCRIPT;
- result.script_type = gds;
- found = true;
- } else {
- Ref<Script> scr = cn->value;
- if (scr.is_valid()) {
- result.kind = DataType::SCRIPT;
- result.script_type = scr;
- found = true;
- }
- }
+ // Also switch multiline mode on here for infix operators.
+ switch (current.type) {
+ // case GDScriptTokenizer::Token::BRACE_OPEN: // Not an infix operator.
+ case GDScriptTokenizer::Token::PARENTHESIS_OPEN:
+ case GDScriptTokenizer::Token::BRACKET_OPEN:
+ push_multiline(true);
break;
- }
-
- // Inner classes
- ClassNode *outer_class = p;
- while (outer_class) {
- if (outer_class->name == id) {
- found = true;
- result.kind = DataType::CLASS;
- result.class_type = outer_class;
- break;
- }
- for (int i = 0; i < outer_class->subclasses.size(); i++) {
- if (outer_class->subclasses[i] == p) {
- continue;
- }
- if (outer_class->subclasses[i]->name == id) {
- found = true;
- result.kind = DataType::CLASS;
- result.class_type = outer_class->subclasses[i];
- break;
- }
- }
- if (found) {
- break;
- }
- outer_class = outer_class->owner;
- }
-
- if (!found && p->base_type.kind == DataType::CLASS) {
- p = p->base_type.class_type;
- } else {
- base_type = p->base_type;
- break;
- }
- }
-
- // Still look for class constants in parent scripts
- if (!found && (base_type.kind == DataType::GDSCRIPT || base_type.kind == DataType::SCRIPT)) {
- Ref<Script> scr = base_type.script_type;
- ERR_FAIL_COND_V(scr.is_null(), result);
- while (scr.is_valid()) {
- Map<StringName, Variant> constants;
- scr->get_constants(&constants);
-
- if (constants.has(id)) {
- Ref<GDScript> gds = constants[id];
-
- if (gds.is_valid()) {
- result.kind = DataType::GDSCRIPT;
- result.script_type = gds;
- found = true;
- } else {
- Ref<Script> scr2 = constants[id];
- if (scr2.is_valid()) {
- result.kind = DataType::SCRIPT;
- result.script_type = scr2;
- found = true;
- }
- }
- }
- if (found) {
- break;
- } else {
- scr = scr->get_base_script();
- }
- }
+ default:
+ break; // Nothing to do.
}
+ token = advance();
+ ParseFunction infix_rule = get_rule(token.type)->infix;
+ previous_operand = (this->*infix_rule)(previous_operand, p_can_assign);
+ }
- if (!found && !for_completion) {
- String base;
- if (name_part == 0) {
- base = "self";
- } else {
- base = result.to_string();
- }
- _set_error("The identifier \"" + String(id) + "\" isn't a valid type (not a script or class), or couldn't be found on base \"" +
- base + "\".",
- p_line);
- return DataType();
- }
+ return previous_operand;
+}
- name_part++;
- }
+GDScriptParser::ExpressionNode *GDScriptParser::parse_expression(bool p_can_assign, bool p_stop_on_assign) {
+ return parse_precedence(PREC_ASSIGNMENT, p_can_assign, p_stop_on_assign);
+}
- return result;
+GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() {
+ return static_cast<IdentifierNode *>(parse_identifier(nullptr, false));
}
-GDScriptParser::DataType GDScriptParser::_type_from_variant(const Variant &p_value) const {
- DataType result;
- result.has_type = true;
- result.is_constant = true;
- result.kind = DataType::BUILTIN;
- result.builtin_type = p_value.get_type();
+GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ if (!previous.is_identifier()) {
+ ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
+ }
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = previous.get_identifier();
- if (result.builtin_type == Variant::OBJECT) {
- Object *obj = p_value.operator Object *();
- if (!obj) {
- return DataType();
- }
- result.native_type = obj->get_class_name();
- Ref<Script> scr = p_value;
- if (scr.is_valid()) {
- result.is_meta_type = true;
- } else {
- result.is_meta_type = false;
- scr = obj->get_script();
- }
- if (scr.is_valid()) {
- result.script_type = scr;
- Ref<GDScript> gds = scr;
- if (gds.is_valid()) {
- result.kind = DataType::GDSCRIPT;
- } else {
- result.kind = DataType::SCRIPT;
- }
- result.native_type = scr->get_instance_base_type();
- } else {
- result.kind = DataType::NATIVE;
+ if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
+ const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
+ switch (declaration.type) {
+ case SuiteNode::Local::CONSTANT:
+ identifier->source = IdentifierNode::LOCAL_CONSTANT;
+ identifier->constant_source = declaration.constant;
+ declaration.constant->usages++;
+ break;
+ case SuiteNode::Local::VARIABLE:
+ identifier->source = IdentifierNode::LOCAL_VARIABLE;
+ identifier->variable_source = declaration.variable;
+ declaration.variable->usages++;
+ break;
+ case SuiteNode::Local::PARAMETER:
+ identifier->source = IdentifierNode::FUNCTION_PARAMETER;
+ identifier->parameter_source = declaration.parameter;
+ declaration.parameter->usages++;
+ break;
+ case SuiteNode::Local::FOR_VARIABLE:
+ identifier->source = IdentifierNode::LOCAL_ITERATOR;
+ identifier->bind_source = declaration.bind;
+ declaration.bind->usages++;
+ break;
+ case SuiteNode::Local::PATTERN_BIND:
+ identifier->source = IdentifierNode::LOCAL_BIND;
+ identifier->bind_source = declaration.bind;
+ declaration.bind->usages++;
+ break;
+ case SuiteNode::Local::UNDEFINED:
+ ERR_FAIL_V_MSG(nullptr, "Undefined local found.");
}
}
- return result;
+ return identifier;
}
-GDScriptParser::DataType GDScriptParser::_type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant) const {
- DataType ret;
- if (p_property.type == Variant::NIL && (p_nil_is_variant || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
- // Variant
- return ret;
- }
- ret.has_type = true;
- ret.builtin_type = p_property.type;
- if (p_property.type == Variant::OBJECT) {
- ret.kind = DataType::NATIVE;
- ret.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
- } else {
- ret.kind = DataType::BUILTIN;
- }
- return ret;
+GDScriptParser::LiteralNode *GDScriptParser::parse_literal() {
+ return static_cast<LiteralNode *>(parse_literal(nullptr, false));
}
-GDScriptParser::DataType GDScriptParser::_type_from_gdtype(const GDScriptDataType &p_gdtype) const {
- DataType result;
- if (!p_gdtype.has_type) {
- return result;
+GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ if (previous.type != GDScriptTokenizer::Token::LITERAL) {
+ push_error("Parser bug: parsing literal node without literal token.");
+ ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
}
- result.has_type = true;
- result.builtin_type = p_gdtype.builtin_type;
- result.native_type = p_gdtype.native_type;
- result.script_type = p_gdtype.script_type;
-
- switch (p_gdtype.kind) {
- case GDScriptDataType::UNINITIALIZED: {
- ERR_PRINT("Uninitialized datatype. Please report a bug.");
- } break;
- case GDScriptDataType::BUILTIN: {
- result.kind = DataType::BUILTIN;
- } break;
- case GDScriptDataType::NATIVE: {
- result.kind = DataType::NATIVE;
- } break;
- case GDScriptDataType::GDSCRIPT: {
- result.kind = DataType::GDSCRIPT;
- } break;
- case GDScriptDataType::SCRIPT: {
- result.kind = DataType::SCRIPT;
- } break;
- }
- return result;
+ LiteralNode *literal = alloc_node<LiteralNode>();
+ literal->value = previous.literal;
+ return literal;
}
-GDScriptParser::DataType GDScriptParser::_get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const {
- if (!p_a.has_type || !p_b.has_type) {
- r_valid = true;
- return DataType();
+GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ if (current_function && current_function->is_static) {
+ push_error(R"(Cannot use "self" inside a static function.)");
}
+ SelfNode *self = alloc_node<SelfNode>();
+ self->current_class = current_class;
+ return self;
+}
- Variant::Type a_type = p_a.kind == DataType::BUILTIN ? p_a.builtin_type : Variant::OBJECT;
- Variant::Type b_type = p_b.kind == DataType::BUILTIN ? p_b.builtin_type : Variant::OBJECT;
+GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ GDScriptTokenizer::Token::Type op_type = previous.type;
+ LiteralNode *constant = alloc_node<LiteralNode>();
- Variant a;
- REF a_ref;
- if (a_type == Variant::OBJECT) {
- a_ref.instance();
- a = a_ref;
- } else {
- Callable::CallError err;
- a = Variant::construct(a_type, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- return DataType();
- }
- }
- Variant b;
- REF b_ref;
- if (b_type == Variant::OBJECT) {
- b_ref.instance();
- b = b_ref;
- } else {
- Callable::CallError err;
- b = Variant::construct(b_type, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- return DataType();
- }
+ switch (op_type) {
+ case GDScriptTokenizer::Token::CONST_PI:
+ constant->value = Math_PI;
+ break;
+ case GDScriptTokenizer::Token::CONST_TAU:
+ constant->value = Math_TAU;
+ break;
+ case GDScriptTokenizer::Token::CONST_INF:
+ constant->value = Math_INF;
+ break;
+ case GDScriptTokenizer::Token::CONST_NAN:
+ constant->value = Math_NAN;
+ break;
+ default:
+ return nullptr; // Unreachable.
}
- // Avoid division by zero
- if (a_type == Variant::INT || a_type == Variant::FLOAT) {
- Variant::evaluate(Variant::OP_ADD, a, 1, a, r_valid);
- }
- if (b_type == Variant::INT || b_type == Variant::FLOAT) {
- Variant::evaluate(Variant::OP_ADD, b, 1, b, r_valid);
- }
- if (a_type == Variant::STRING && b_type != Variant::ARRAY) {
- a = "%s"; // Work around for formatting operator (%)
- }
+ return constant;
+}
- Variant ret;
- Variant::evaluate(p_op, a, b, ret, r_valid);
+GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ GDScriptTokenizer::Token::Type op_type = previous.type;
+ UnaryOpNode *operation = alloc_node<UnaryOpNode>();
- if (r_valid) {
- return _type_from_variant(ret);
+ switch (op_type) {
+ case GDScriptTokenizer::Token::MINUS:
+ operation->operation = UnaryOpNode::OP_NEGATIVE;
+ operation->variant_op = Variant::OP_NEGATE;
+ operation->operand = parse_precedence(PREC_SIGN, false);
+ break;
+ case GDScriptTokenizer::Token::PLUS:
+ operation->operation = UnaryOpNode::OP_POSITIVE;
+ operation->variant_op = Variant::OP_POSITIVE;
+ operation->operand = parse_precedence(PREC_SIGN, false);
+ break;
+ case GDScriptTokenizer::Token::TILDE:
+ operation->operation = UnaryOpNode::OP_COMPLEMENT;
+ operation->variant_op = Variant::OP_BIT_NEGATE;
+ operation->operand = parse_precedence(PREC_BIT_NOT, false);
+ break;
+ case GDScriptTokenizer::Token::NOT:
+ case GDScriptTokenizer::Token::BANG:
+ operation->operation = UnaryOpNode::OP_LOGIC_NOT;
+ operation->variant_op = Variant::OP_NOT;
+ operation->operand = parse_precedence(PREC_LOGIC_NOT, false);
+ break;
+ default:
+ return nullptr; // Unreachable.
}
- return DataType();
+ return operation;
}
-Variant::Operator GDScriptParser::_get_variant_operation(const OperatorNode::Operator &p_op) const {
- switch (p_op) {
- case OperatorNode::OP_NEG: {
- return Variant::OP_NEGATE;
- } break;
- case OperatorNode::OP_POS: {
- return Variant::OP_POSITIVE;
- } break;
- case OperatorNode::OP_NOT: {
- return Variant::OP_NOT;
- } break;
- case OperatorNode::OP_BIT_INVERT: {
- return Variant::OP_BIT_NEGATE;
- } break;
- case OperatorNode::OP_IN: {
- return Variant::OP_IN;
- } break;
- case OperatorNode::OP_EQUAL: {
- return Variant::OP_EQUAL;
- } break;
- case OperatorNode::OP_NOT_EQUAL: {
- return Variant::OP_NOT_EQUAL;
- } break;
- case OperatorNode::OP_LESS: {
- return Variant::OP_LESS;
- } break;
- case OperatorNode::OP_LESS_EQUAL: {
- return Variant::OP_LESS_EQUAL;
- } break;
- case OperatorNode::OP_GREATER: {
- return Variant::OP_GREATER;
- } break;
- case OperatorNode::OP_GREATER_EQUAL: {
- return Variant::OP_GREATER_EQUAL;
- } break;
- case OperatorNode::OP_AND: {
- return Variant::OP_AND;
- } break;
- case OperatorNode::OP_OR: {
- return Variant::OP_OR;
- } break;
- case OperatorNode::OP_ASSIGN_ADD:
- case OperatorNode::OP_ADD: {
- return Variant::OP_ADD;
- } break;
- case OperatorNode::OP_ASSIGN_SUB:
- case OperatorNode::OP_SUB: {
- return Variant::OP_SUBTRACT;
- } break;
- case OperatorNode::OP_ASSIGN_MUL:
- case OperatorNode::OP_MUL: {
- return Variant::OP_MULTIPLY;
- } break;
- case OperatorNode::OP_ASSIGN_DIV:
- case OperatorNode::OP_DIV: {
- return Variant::OP_DIVIDE;
- } break;
- case OperatorNode::OP_ASSIGN_MOD:
- case OperatorNode::OP_MOD: {
- return Variant::OP_MODULE;
- } break;
- case OperatorNode::OP_ASSIGN_BIT_AND:
- case OperatorNode::OP_BIT_AND: {
- return Variant::OP_BIT_AND;
- } break;
- case OperatorNode::OP_ASSIGN_BIT_OR:
- case OperatorNode::OP_BIT_OR: {
- return Variant::OP_BIT_OR;
- } break;
- case OperatorNode::OP_ASSIGN_BIT_XOR:
- case OperatorNode::OP_BIT_XOR: {
- return Variant::OP_BIT_XOR;
- } break;
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- case OperatorNode::OP_SHIFT_LEFT: {
- return Variant::OP_SHIFT_LEFT;
- }
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- case OperatorNode::OP_SHIFT_RIGHT: {
- return Variant::OP_SHIFT_RIGHT;
- }
- default: {
- return Variant::OP_MAX;
- } break;
- }
-}
+GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ GDScriptTokenizer::Token op = previous;
+ BinaryOpNode *operation = alloc_node<BinaryOpNode>();
-bool GDScriptParser::_is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion) const {
- // Ignore for completion
- if (!check_types || for_completion) {
- return true;
+ Precedence precedence = (Precedence)(get_rule(op.type)->precedence + 1);
+ operation->left_operand = p_previous_operand;
+ operation->right_operand = parse_precedence(precedence, false);
+
+ if (operation->right_operand == nullptr) {
+ push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name()));
}
- // Can't test if not all have type
- if (!p_container.has_type || !p_expression.has_type) {
- return true;
+
+ // TODO: Also for unary, ternary, and assignment.
+ switch (op.type) {
+ case GDScriptTokenizer::Token::PLUS:
+ operation->operation = BinaryOpNode::OP_ADDITION;
+ operation->variant_op = Variant::OP_ADD;
+ break;
+ case GDScriptTokenizer::Token::MINUS:
+ operation->operation = BinaryOpNode::OP_SUBTRACTION;
+ operation->variant_op = Variant::OP_SUBTRACT;
+ break;
+ case GDScriptTokenizer::Token::STAR:
+ operation->operation = BinaryOpNode::OP_MULTIPLICATION;
+ operation->variant_op = Variant::OP_MULTIPLY;
+ break;
+ case GDScriptTokenizer::Token::SLASH:
+ operation->operation = BinaryOpNode::OP_DIVISION;
+ operation->variant_op = Variant::OP_DIVIDE;
+ break;
+ case GDScriptTokenizer::Token::PERCENT:
+ operation->operation = BinaryOpNode::OP_MODULO;
+ operation->variant_op = Variant::OP_MODULE;
+ break;
+ case GDScriptTokenizer::Token::LESS_LESS:
+ operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT;
+ operation->variant_op = Variant::OP_SHIFT_LEFT;
+ break;
+ case GDScriptTokenizer::Token::GREATER_GREATER:
+ operation->operation = BinaryOpNode::OP_BIT_RIGHT_SHIFT;
+ operation->variant_op = Variant::OP_SHIFT_RIGHT;
+ break;
+ case GDScriptTokenizer::Token::AMPERSAND:
+ operation->operation = BinaryOpNode::OP_BIT_AND;
+ operation->variant_op = Variant::OP_BIT_AND;
+ break;
+ case GDScriptTokenizer::Token::PIPE:
+ operation->operation = BinaryOpNode::OP_BIT_OR;
+ operation->variant_op = Variant::OP_BIT_OR;
+ break;
+ case GDScriptTokenizer::Token::CARET:
+ operation->operation = BinaryOpNode::OP_BIT_XOR;
+ operation->variant_op = Variant::OP_BIT_XOR;
+ break;
+ case GDScriptTokenizer::Token::AND:
+ case GDScriptTokenizer::Token::AMPERSAND_AMPERSAND:
+ operation->operation = BinaryOpNode::OP_LOGIC_AND;
+ operation->variant_op = Variant::OP_AND;
+ break;
+ case GDScriptTokenizer::Token::OR:
+ case GDScriptTokenizer::Token::PIPE_PIPE:
+ operation->operation = BinaryOpNode::OP_LOGIC_OR;
+ operation->variant_op = Variant::OP_OR;
+ break;
+ case GDScriptTokenizer::Token::IS:
+ operation->operation = BinaryOpNode::OP_TYPE_TEST;
+ break;
+ case GDScriptTokenizer::Token::IN:
+ operation->operation = BinaryOpNode::OP_CONTENT_TEST;
+ operation->variant_op = Variant::OP_IN;
+ break;
+ case GDScriptTokenizer::Token::EQUAL_EQUAL:
+ operation->operation = BinaryOpNode::OP_COMP_EQUAL;
+ operation->variant_op = Variant::OP_EQUAL;
+ break;
+ case GDScriptTokenizer::Token::BANG_EQUAL:
+ operation->operation = BinaryOpNode::OP_COMP_NOT_EQUAL;
+ operation->variant_op = Variant::OP_NOT_EQUAL;
+ break;
+ case GDScriptTokenizer::Token::LESS:
+ operation->operation = BinaryOpNode::OP_COMP_LESS;
+ operation->variant_op = Variant::OP_LESS;
+ break;
+ case GDScriptTokenizer::Token::LESS_EQUAL:
+ operation->operation = BinaryOpNode::OP_COMP_LESS_EQUAL;
+ operation->variant_op = Variant::OP_LESS_EQUAL;
+ break;
+ case GDScriptTokenizer::Token::GREATER:
+ operation->operation = BinaryOpNode::OP_COMP_GREATER;
+ operation->variant_op = Variant::OP_GREATER;
+ break;
+ case GDScriptTokenizer::Token::GREATER_EQUAL:
+ operation->operation = BinaryOpNode::OP_COMP_GREATER_EQUAL;
+ operation->variant_op = Variant::OP_GREATER_EQUAL;
+ break;
+ default:
+ return nullptr; // Unreachable.
}
- // Should never get here unresolved
- ERR_FAIL_COND_V(p_container.kind == DataType::UNRESOLVED, false);
- ERR_FAIL_COND_V(p_expression.kind == DataType::UNRESOLVED, false);
+ return operation;
+}
- if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) {
- bool valid = p_container.builtin_type == p_expression.builtin_type;
- if (p_allow_implicit_conversion) {
- valid = valid || Variant::can_convert_strict(p_expression.builtin_type, p_container.builtin_type);
- }
- return valid;
- }
+GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ // Only one ternary operation exists, so no abstraction here.
+ TernaryOpNode *operation = alloc_node<TernaryOpNode>();
+ operation->true_expr = p_previous_operand;
- if (p_container.kind == DataType::BUILTIN && p_container.builtin_type == Variant::OBJECT) {
- // Object built-in is a special case, it's compatible with any object and with null
- if (p_expression.kind == DataType::BUILTIN) {
- return p_expression.builtin_type == Variant::NIL;
- }
- // If it's not a built-in, must be an object
- return true;
- }
+ operation->condition = parse_precedence(PREC_TERNARY, false);
- if (p_container.kind == DataType::BUILTIN || (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type != Variant::NIL)) {
- // Can't mix built-ins with objects
- return false;
+ if (operation->condition == nullptr) {
+ push_error(R"(Expected expression as ternary condition after "if".)");
}
- // From now on everything is objects, check polymorphism
- // The container must be the same class or a superclass of the expression
+ consume(GDScriptTokenizer::Token::ELSE, R"(Expected "else" after ternary operator condition.)");
- if (p_expression.kind == DataType::BUILTIN && p_expression.builtin_type == Variant::NIL) {
- // Null can be assigned to object types
- return true;
+ operation->false_expr = parse_precedence(PREC_TERNARY, false);
+
+ return operation;
+}
+
+GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ if (!p_can_assign) {
+ push_error("Assignment is not allowed inside an expression.");
+ return parse_expression(false); // Return the following expression.
}
- StringName expr_native;
- Ref<Script> expr_script;
- ClassNode *expr_class = nullptr;
+#ifdef DEBUG_ENABLED
+ VariableNode *source_variable = nullptr;
+#endif
- switch (p_expression.kind) {
- case DataType::NATIVE: {
- if (p_container.kind != DataType::NATIVE) {
- // Non-native type can't be a superclass of a native type
- return false;
- }
- if (p_expression.is_meta_type) {
- expr_native = GDScriptNativeClass::get_class_static();
- } else {
- expr_native = p_expression.native_type;
- }
- } break;
- case DataType::SCRIPT:
- case DataType::GDSCRIPT: {
- if (p_container.kind == DataType::CLASS) {
- // This cannot be resolved without cyclic dependencies, so just bail out
- return false;
- }
- if (p_expression.is_meta_type) {
- expr_native = p_expression.script_type->get_class_name();
- } else {
- expr_script = p_expression.script_type;
- expr_native = expr_script->get_instance_base_type();
- }
- } break;
- case DataType::CLASS: {
- if (p_expression.is_meta_type) {
- expr_native = GDScript::get_class_static();
- } else {
- expr_class = p_expression.class_type;
- ClassNode *base = expr_class;
- while (base->base_type.kind == DataType::CLASS) {
- base = base->base_type.class_type;
- }
- expr_native = base->base_type.native_type;
- expr_script = base->base_type.script_type;
+ switch (p_previous_operand->type) {
+ case Node::IDENTIFIER: {
+#ifdef DEBUG_ENABLED
+ // Get source to store assignment count.
+ // Also remove one usage since assignment isn't usage.
+ IdentifierNode *id = static_cast<IdentifierNode *>(p_previous_operand);
+ switch (id->source) {
+ case IdentifierNode::LOCAL_VARIABLE:
+
+ source_variable = id->variable_source;
+ id->variable_source->usages--;
+ break;
+ case IdentifierNode::LOCAL_CONSTANT:
+ id->constant_source->usages--;
+ break;
+ case IdentifierNode::FUNCTION_PARAMETER:
+ id->parameter_source->usages--;
+ break;
+ case IdentifierNode::LOCAL_ITERATOR:
+ case IdentifierNode::LOCAL_BIND:
+ id->bind_source->usages--;
+ break;
+ default:
+ break;
}
+#endif
} break;
- case DataType::BUILTIN: // Already handled above
- case DataType::UNRESOLVED: // Not allowed, see above
+ case Node::SUBSCRIPT:
+ // Okay.
break;
+ default:
+ push_error(R"(Only identifier, attribute access, and subscription access can be used as assignment target.)");
+ return parse_expression(false); // Return the following expression.
}
- // Some classes are prefixed with `_` internally
- if (!ClassDB::class_exists(expr_native)) {
- expr_native = "_" + expr_native;
+ AssignmentNode *assignment = alloc_node<AssignmentNode>();
+ make_completion_context(COMPLETION_ASSIGN, assignment);
+#ifdef DEBUG_ENABLED
+ bool has_operator = true;
+#endif
+ switch (previous.type) {
+ case GDScriptTokenizer::Token::EQUAL:
+ assignment->operation = AssignmentNode::OP_NONE;
+ assignment->variant_op = Variant::OP_MAX;
+#ifdef DEBUG_ENABLED
+ has_operator = false;
+#endif
+ break;
+ case GDScriptTokenizer::Token::PLUS_EQUAL:
+ assignment->operation = AssignmentNode::OP_ADDITION;
+ assignment->variant_op = Variant::OP_ADD;
+ break;
+ case GDScriptTokenizer::Token::MINUS_EQUAL:
+ assignment->operation = AssignmentNode::OP_SUBTRACTION;
+ assignment->variant_op = Variant::OP_SUBTRACT;
+ break;
+ case GDScriptTokenizer::Token::STAR_EQUAL:
+ assignment->operation = AssignmentNode::OP_MULTIPLICATION;
+ assignment->variant_op = Variant::OP_MULTIPLY;
+ break;
+ case GDScriptTokenizer::Token::SLASH_EQUAL:
+ assignment->operation = AssignmentNode::OP_DIVISION;
+ assignment->variant_op = Variant::OP_DIVIDE;
+ break;
+ case GDScriptTokenizer::Token::PERCENT_EQUAL:
+ assignment->operation = AssignmentNode::OP_MODULO;
+ assignment->variant_op = Variant::OP_MODULE;
+ break;
+ case GDScriptTokenizer::Token::LESS_LESS_EQUAL:
+ assignment->operation = AssignmentNode::OP_BIT_SHIFT_LEFT;
+ assignment->variant_op = Variant::OP_SHIFT_LEFT;
+ break;
+ case GDScriptTokenizer::Token::GREATER_GREATER_EQUAL:
+ assignment->operation = AssignmentNode::OP_BIT_SHIFT_RIGHT;
+ assignment->variant_op = Variant::OP_SHIFT_RIGHT;
+ break;
+ case GDScriptTokenizer::Token::AMPERSAND_EQUAL:
+ assignment->operation = AssignmentNode::OP_BIT_AND;
+ assignment->variant_op = Variant::OP_BIT_AND;
+ break;
+ case GDScriptTokenizer::Token::PIPE_EQUAL:
+ assignment->operation = AssignmentNode::OP_BIT_OR;
+ assignment->variant_op = Variant::OP_BIT_OR;
+ break;
+ case GDScriptTokenizer::Token::CARET_EQUAL:
+ assignment->operation = AssignmentNode::OP_BIT_XOR;
+ assignment->variant_op = Variant::OP_BIT_XOR;
+ break;
+ default:
+ break; // Unreachable.
}
+ assignment->assignee = p_previous_operand;
+ assignment->assigned_value = parse_expression(false);
- switch (p_container.kind) {
- case DataType::NATIVE: {
- if (p_container.is_meta_type) {
- return ClassDB::is_parent_class(expr_native, GDScriptNativeClass::get_class_static());
- } else {
- StringName container_native = ClassDB::class_exists(p_container.native_type) ? p_container.native_type : StringName("_" + p_container.native_type);
- return ClassDB::is_parent_class(expr_native, container_native);
- }
- } break;
- case DataType::SCRIPT:
- case DataType::GDSCRIPT: {
- if (p_container.is_meta_type) {
- return ClassDB::is_parent_class(expr_native, GDScript::get_class_static());
- }
- if (expr_class == head && p_container.script_type->get_path() == self_path) {
- // Special case: container is self script and expression is self
- return true;
- }
- while (expr_script.is_valid()) {
- if (expr_script == p_container.script_type) {
- return true;
- }
- expr_script = expr_script->get_base_script();
- }
- return false;
- } break;
- case DataType::CLASS: {
- if (p_container.is_meta_type) {
- return ClassDB::is_parent_class(expr_native, GDScript::get_class_static());
- }
- if (p_container.class_type == head && expr_script.is_valid() && expr_script->get_path() == self_path) {
- // Special case: container is self and expression is self script
- return true;
- }
- while (expr_class) {
- if (expr_class == p_container.class_type) {
- return true;
- }
- expr_class = expr_class->base_type.class_type;
- }
- return false;
- } break;
- case DataType::BUILTIN: // Already handled above
- case DataType::UNRESOLVED: // Not allowed, see above
- break;
+#ifdef DEBUG_ENABLED
+ if (has_operator && source_variable != nullptr && source_variable->assignments == 0) {
+ push_warning(assignment, GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, source_variable->identifier->name);
}
+#endif
- return false;
+ return assignment;
}
-GDScriptParser::Node *GDScriptParser::_get_default_value_for_type(const DataType &p_type, int p_line) {
- Node *result;
+GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ AwaitNode *await = alloc_node<AwaitNode>();
+ await->to_await = parse_precedence(PREC_AWAIT, false);
- if (p_type.has_type && p_type.kind == DataType::BUILTIN && p_type.builtin_type != Variant::NIL && p_type.builtin_type != Variant::OBJECT) {
- if (p_type.builtin_type == Variant::ARRAY) {
- result = alloc_node<ArrayNode>();
- } else if (p_type.builtin_type == Variant::DICTIONARY) {
- result = alloc_node<DictionaryNode>();
- } else {
- ConstantNode *c = alloc_node<ConstantNode>();
- Callable::CallError err;
- c->value = Variant::construct(p_type.builtin_type, nullptr, 0, err);
- c->datatype = _type_from_variant(c->value);
- result = c;
- }
- } else {
- ConstantNode *c = alloc_node<ConstantNode>();
- c->value = Variant();
- c->datatype = _type_from_variant(c->value);
- result = c;
- }
-
- result->line = p_line;
+ current_function->is_coroutine = true;
- return result;
+ return await;
}
-GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
-#ifdef DEBUG_ENABLED
- if (p_node->get_datatype().has_type && p_node->type != Node::TYPE_ARRAY && p_node->type != Node::TYPE_DICTIONARY) {
-#else
- if (p_node->get_datatype().has_type) {
-#endif
- return p_node->get_datatype();
- }
-
- DataType node_type;
+GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ ArrayNode *array = alloc_node<ArrayNode>();
- switch (p_node->type) {
- case Node::TYPE_CONSTANT: {
- node_type = _type_from_variant(static_cast<ConstantNode *>(p_node)->value);
- } break;
- case Node::TYPE_TYPE: {
- TypeNode *tn = static_cast<TypeNode *>(p_node);
- node_type.has_type = true;
- node_type.is_meta_type = true;
- node_type.kind = DataType::BUILTIN;
- node_type.builtin_type = tn->vtype;
- } break;
- case Node::TYPE_ARRAY: {
- node_type.has_type = true;
- node_type.kind = DataType::BUILTIN;
- node_type.builtin_type = Variant::ARRAY;
-#ifdef DEBUG_ENABLED
- // Check stuff inside the array
- ArrayNode *an = static_cast<ArrayNode *>(p_node);
- for (int i = 0; i < an->elements.size(); i++) {
- _reduce_node_type(an->elements[i]);
- }
-#endif // DEBUG_ENABLED
- } break;
- case Node::TYPE_DICTIONARY: {
- node_type.has_type = true;
- node_type.kind = DataType::BUILTIN;
- node_type.builtin_type = Variant::DICTIONARY;
-#ifdef DEBUG_ENABLED
- // Check stuff inside the dictionarty
- DictionaryNode *dn = static_cast<DictionaryNode *>(p_node);
- for (int i = 0; i < dn->elements.size(); i++) {
- _reduce_node_type(dn->elements[i].key);
- _reduce_node_type(dn->elements[i].value);
- }
-#endif // DEBUG_ENABLED
- } break;
- case Node::TYPE_SELF: {
- node_type.has_type = true;
- node_type.kind = DataType::CLASS;
- node_type.class_type = current_class;
- node_type.is_constant = true;
- } break;
- case Node::TYPE_IDENTIFIER: {
- IdentifierNode *id = static_cast<IdentifierNode *>(p_node);
- if (id->declared_block) {
- node_type = id->declared_block->variables[id->name]->get_datatype();
- id->declared_block->variables[id->name]->usages += 1;
- } else if (id->name == "#match_value") {
- // It's a special id just for the match statetement, ignore
+ if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
+ do {
+ if (check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
+ // Allow for trailing comma.
break;
- } else if (current_function && current_function->arguments.find(id->name) >= 0) {
- int idx = current_function->arguments.find(id->name);
- node_type = current_function->argument_types[idx];
- } else {
- node_type = _reduce_identifier_type(nullptr, id->name, id->line, false);
}
- } break;
- case Node::TYPE_CAST: {
- CastNode *cn = static_cast<CastNode *>(p_node);
-
- DataType source_type = _reduce_node_type(cn->source_node);
- cn->cast_type = _resolve_type(cn->cast_type, cn->line);
- if (source_type.has_type) {
- bool valid = false;
- if (check_types) {
- if (cn->cast_type.kind == DataType::BUILTIN && source_type.kind == DataType::BUILTIN) {
- valid = Variant::can_convert(source_type.builtin_type, cn->cast_type.builtin_type);
- }
- if (cn->cast_type.kind != DataType::BUILTIN && source_type.kind != DataType::BUILTIN) {
- valid = _is_type_compatible(cn->cast_type, source_type) || _is_type_compatible(source_type, cn->cast_type);
- }
- if (!valid) {
- _set_error("Invalid cast. Cannot convert from \"" + source_type.to_string() +
- "\" to \"" + cn->cast_type.to_string() + "\".",
- cn->line);
- return DataType();
- }
- }
+ ExpressionNode *element = parse_expression(false);
+ if (element == nullptr) {
+ push_error(R"(Expected expression as array element.)");
} else {
-#ifdef DEBUG_ENABLED
- _add_warning(GDScriptWarning::UNSAFE_CAST, cn->line, cn->cast_type.to_string());
-#endif // DEBUG_ENABLED
- _mark_line_as_unsafe(cn->line);
+ array->elements.push_back(element);
}
+ } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
+ }
+ pop_multiline();
+ consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)");
- node_type = cn->cast_type;
-
- } break;
- case Node::TYPE_OPERATOR: {
- OperatorNode *op = static_cast<OperatorNode *>(p_node);
-
- switch (op->op) {
- case OperatorNode::OP_CALL:
- case OperatorNode::OP_PARENT_CALL: {
- node_type = _reduce_function_call_type(op);
- } break;
- case OperatorNode::OP_YIELD: {
- if (op->arguments.size() == 2) {
- DataType base_type = _reduce_node_type(op->arguments[0]);
- DataType signal_type = _reduce_node_type(op->arguments[1]);
- // TODO: Check if signal exists when it's a constant
- if (base_type.has_type && base_type.kind == DataType::BUILTIN && base_type.builtin_type != Variant::NIL && base_type.builtin_type != Variant::OBJECT) {
- _set_error("The first argument of \"yield()\" must be an object.", op->line);
- return DataType();
- }
- if (signal_type.has_type && (signal_type.kind != DataType::BUILTIN || signal_type.builtin_type != Variant::STRING)) {
- _set_error("The second argument of \"yield()\" must be a string.", op->line);
- return DataType();
- }
- }
- // yield can return anything
- node_type.has_type = false;
- } break;
- case OperatorNode::OP_IS:
- case OperatorNode::OP_IS_BUILTIN: {
- if (op->arguments.size() != 2) {
- _set_error("Parser bug: binary operation without 2 arguments.", op->line);
- ERR_FAIL_V(DataType());
- }
-
- DataType value_type = _reduce_node_type(op->arguments[0]);
- DataType type_type = _reduce_node_type(op->arguments[1]);
-
- if (check_types && type_type.has_type) {
- if (!type_type.is_meta_type && (type_type.kind != DataType::NATIVE || !ClassDB::is_parent_class(type_type.native_type, "Script"))) {
- _set_error("Invalid \"is\" test: the right operand isn't a type (neither a native type nor a script).", op->line);
- return DataType();
- }
- type_type.is_meta_type = false; // Test the actual type
- if (!_is_type_compatible(type_type, value_type) && !_is_type_compatible(value_type, type_type)) {
- if (op->op == OperatorNode::OP_IS) {
- _set_error("A value of type \"" + value_type.to_string() + "\" will never be an instance of \"" + type_type.to_string() + "\".", op->line);
- } else {
- _set_error("A value of type \"" + value_type.to_string() + "\" will never be of type \"" + type_type.to_string() + "\".", op->line);
- }
- return DataType();
- }
- }
+ return array;
+}
- node_type.has_type = true;
- node_type.is_constant = true;
- node_type.is_meta_type = false;
- node_type.kind = DataType::BUILTIN;
- node_type.builtin_type = Variant::BOOL;
- } break;
- // Unary operators
- case OperatorNode::OP_NEG:
- case OperatorNode::OP_POS:
- case OperatorNode::OP_NOT:
- case OperatorNode::OP_BIT_INVERT: {
- DataType argument_type = _reduce_node_type(op->arguments[0]);
- if (!argument_type.has_type) {
- break;
- }
+GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ DictionaryNode *dictionary = alloc_node<DictionaryNode>();
- Variant::Operator var_op = _get_variant_operation(op->op);
- bool valid = false;
- node_type = _get_operation_type(var_op, argument_type, argument_type, valid);
+ bool decided_style = false;
+ if (!check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
+ do {
+ if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
+ // Allow for trailing comma.
+ break;
+ }
- if (check_types && !valid) {
- _set_error("Invalid operand type (\"" + argument_type.to_string() +
- "\") to unary operator \"" + Variant::get_operator_name(var_op) + "\".",
- op->line, op->column);
- return DataType();
- }
+ // Key.
+ ExpressionNode *key = parse_expression(false, true); // Stop on "=" so we can check for Lua table style.
- } break;
- // Binary operators
- case OperatorNode::OP_IN:
- case OperatorNode::OP_EQUAL:
- case OperatorNode::OP_NOT_EQUAL:
- case OperatorNode::OP_LESS:
- case OperatorNode::OP_LESS_EQUAL:
- case OperatorNode::OP_GREATER:
- case OperatorNode::OP_GREATER_EQUAL:
- case OperatorNode::OP_AND:
- case OperatorNode::OP_OR:
- case OperatorNode::OP_ADD:
- case OperatorNode::OP_SUB:
- case OperatorNode::OP_MUL:
- case OperatorNode::OP_DIV:
- case OperatorNode::OP_MOD:
- case OperatorNode::OP_SHIFT_LEFT:
- case OperatorNode::OP_SHIFT_RIGHT:
- case OperatorNode::OP_BIT_AND:
- case OperatorNode::OP_BIT_OR:
- case OperatorNode::OP_BIT_XOR: {
- if (op->arguments.size() != 2) {
- _set_error("Parser bug: binary operation without 2 arguments.", op->line);
- ERR_FAIL_V(DataType());
- }
+ if (key == nullptr) {
+ push_error(R"(Expected expression as dictionary key.)");
+ }
- DataType argument_a_type = _reduce_node_type(op->arguments[0]);
- DataType argument_b_type = _reduce_node_type(op->arguments[1]);
- if (!argument_a_type.has_type || !argument_b_type.has_type) {
- _mark_line_as_unsafe(op->line);
+ if (!decided_style) {
+ switch (current.type) {
+ case GDScriptTokenizer::Token::COLON:
+ dictionary->style = DictionaryNode::PYTHON_DICT;
break;
- }
-
- Variant::Operator var_op = _get_variant_operation(op->op);
- bool valid = false;
- node_type = _get_operation_type(var_op, argument_a_type, argument_b_type, valid);
-
- if (check_types && !valid) {
- _set_error("Invalid operand types (\"" + argument_a_type.to_string() + "\" and \"" +
- argument_b_type.to_string() + "\") to operator \"" + Variant::get_operator_name(var_op) + "\".",
- op->line, op->column);
- return DataType();
- }
-#ifdef DEBUG_ENABLED
- if (var_op == Variant::OP_DIVIDE && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT &&
- argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) {
- _add_warning(GDScriptWarning::INTEGER_DIVISION, op->line);
- }
-#endif // DEBUG_ENABLED
-
- } break;
- // Ternary operators
- case OperatorNode::OP_TERNARY_IF: {
- if (op->arguments.size() != 3) {
- _set_error("Parser bug: ternary operation without 3 arguments.");
- ERR_FAIL_V(DataType());
- }
+ case GDScriptTokenizer::Token::EQUAL:
+ dictionary->style = DictionaryNode::LUA_TABLE;
+ break;
+ default:
+ push_error(R"(Expected ":" or "=" after dictionary key.)");
+ break;
+ }
+ decided_style = true;
+ }
- DataType true_type = _reduce_node_type(op->arguments[1]);
- DataType false_type = _reduce_node_type(op->arguments[2]);
- // Check arguments[0] errors.
- _reduce_node_type(op->arguments[0]);
-
- // If types are equal, then the expression is of the same type
- // If they are compatible, return the broader type
- if (true_type == false_type || _is_type_compatible(true_type, false_type)) {
- node_type = true_type;
- } else if (_is_type_compatible(false_type, true_type)) {
- node_type = false_type;
- } else {
-#ifdef DEBUG_ENABLED
- _add_warning(GDScriptWarning::INCOMPATIBLE_TERNARY, op->line);
-#endif // DEBUG_ENABLED
+ switch (dictionary->style) {
+ case DictionaryNode::LUA_TABLE:
+ if (key != nullptr && key->type != Node::IDENTIFIER) {
+ push_error("Expected identifier as dictionary key.");
}
- } break;
- // Assignment should never happen within an expression
- case OperatorNode::OP_ASSIGN:
- case OperatorNode::OP_ASSIGN_ADD:
- case OperatorNode::OP_ASSIGN_SUB:
- case OperatorNode::OP_ASSIGN_MUL:
- case OperatorNode::OP_ASSIGN_DIV:
- case OperatorNode::OP_ASSIGN_MOD:
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- case OperatorNode::OP_ASSIGN_BIT_AND:
- case OperatorNode::OP_ASSIGN_BIT_OR:
- case OperatorNode::OP_ASSIGN_BIT_XOR:
- case OperatorNode::OP_INIT_ASSIGN: {
- _set_error("Assignment inside an expression isn't allowed (parser bug?).", op->line);
- return DataType();
-
- } break;
- case OperatorNode::OP_INDEX_NAMED: {
- if (op->arguments.size() != 2) {
- _set_error("Parser bug: named index with invalid arguments.", op->line);
- ERR_FAIL_V(DataType());
- }
- if (op->arguments[1]->type != Node::TYPE_IDENTIFIER) {
- _set_error("Parser bug: named index without identifier argument.", op->line);
- ERR_FAIL_V(DataType());
- }
-
- DataType base_type = _reduce_node_type(op->arguments[0]);
- IdentifierNode *member_id = static_cast<IdentifierNode *>(op->arguments[1]);
-
- if (base_type.has_type) {
- if (check_types && base_type.kind == DataType::BUILTIN) {
- // Variant type, just test if it's possible
- DataType result;
- switch (base_type.builtin_type) {
- case Variant::NIL:
- case Variant::DICTIONARY: {
- result.has_type = false;
- } break;
- default: {
- Callable::CallError err;
- Variant temp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
-
- bool valid = false;
- Variant res = temp.get(member_id->name.operator String(), &valid);
-
- if (valid) {
- result = _type_from_variant(res);
- } else if (check_types) {
- _set_error("Can't get index \"" + String(member_id->name.operator String()) + "\" on base \"" +
- base_type.to_string() + "\".",
- op->line);
- return DataType();
- }
- } break;
- }
- result.is_constant = false;
- node_type = result;
+ if (!match(GDScriptTokenizer::Token::EQUAL)) {
+ if (match(GDScriptTokenizer::Token::COLON)) {
+ push_error(R"(Expected "=" after dictionary key. Mixing dictionary styles is not allowed.)");
+ advance(); // Consume wrong separator anyway.
} else {
- node_type = _reduce_identifier_type(&base_type, member_id->name, op->line, true);
-#ifdef DEBUG_ENABLED
- if (!node_type.has_type) {
- _mark_line_as_unsafe(op->line);
- _add_warning(GDScriptWarning::UNSAFE_PROPERTY_ACCESS, op->line, member_id->name.operator String(), base_type.to_string());
- }
-#endif // DEBUG_ENABLED
+ push_error(R"(Expected "=" after dictionary key.)");
}
- } else {
- _mark_line_as_unsafe(op->line);
}
- if (error_set) {
- return DataType();
- }
- } break;
- case OperatorNode::OP_INDEX: {
- if (op->arguments[1]->type == Node::TYPE_CONSTANT) {
- ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[1]);
- if (cn->value.get_type() == Variant::STRING) {
- // Treat this as named indexing
-
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = cn->value.operator StringName();
- id->datatype = cn->datatype;
-
- op->op = OperatorNode::OP_INDEX_NAMED;
- op->arguments.write[1] = id;
-
- return _reduce_node_type(op);
+ break;
+ case DictionaryNode::PYTHON_DICT:
+ if (!match(GDScriptTokenizer::Token::COLON)) {
+ if (match(GDScriptTokenizer::Token::EQUAL)) {
+ push_error(R"(Expected ":" after dictionary key. Mixing dictionary styles is not allowed.)");
+ advance(); // Consume wrong separator anyway.
+ } else {
+ push_error(R"(Expected ":" after dictionary key.)");
}
}
+ break;
+ }
- DataType base_type = _reduce_node_type(op->arguments[0]);
- DataType index_type = _reduce_node_type(op->arguments[1]);
-
- if (!base_type.has_type) {
- _mark_line_as_unsafe(op->line);
- break;
- }
-
- if (check_types && index_type.has_type) {
- if (base_type.kind == DataType::BUILTIN) {
- // Check if indexing is valid
- bool error = index_type.kind != DataType::BUILTIN && base_type.builtin_type != Variant::DICTIONARY;
- if (!error) {
- switch (base_type.builtin_type) {
- // Expect int or real as index
- case Variant::PACKED_BYTE_ARRAY:
- case Variant::PACKED_COLOR_ARRAY:
- case Variant::PACKED_INT32_ARRAY:
- case Variant::PACKED_INT64_ARRAY:
- case Variant::PACKED_FLOAT32_ARRAY:
- case Variant::PACKED_FLOAT64_ARRAY:
- case Variant::PACKED_STRING_ARRAY:
- case Variant::PACKED_VECTOR2_ARRAY:
- case Variant::PACKED_VECTOR3_ARRAY:
- case Variant::ARRAY:
- case Variant::STRING: {
- error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT;
- } break;
- // Expect String only
- case Variant::RECT2:
- case Variant::PLANE:
- case Variant::QUAT:
- case Variant::AABB:
- case Variant::OBJECT: {
- error = index_type.builtin_type != Variant::STRING;
- } break;
- // Expect String or number
- case Variant::VECTOR2:
- case Variant::VECTOR3:
- case Variant::TRANSFORM2D:
- case Variant::BASIS:
- case Variant::TRANSFORM: {
- error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT &&
- index_type.builtin_type != Variant::STRING;
- } break;
- // Expect String or int
- case Variant::COLOR: {
- error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
- } break;
- default: {
- }
- }
- }
- if (error) {
- _set_error("Invalid index type (" + index_type.to_string() + ") for base \"" + base_type.to_string() + "\".",
- op->line);
- return DataType();
- }
+ // Value.
+ ExpressionNode *value = parse_expression(false);
+ if (value == nullptr) {
+ push_error(R"(Expected expression as dictionary value.)");
+ }
- if (op->arguments[1]->type == GDScriptParser::Node::TYPE_CONSTANT) {
- ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[1]);
- // Index is a constant, just try it if possible
- switch (base_type.builtin_type) {
- // Arrays/string have variable indexing, can't test directly
- case Variant::STRING:
- case Variant::ARRAY:
- case Variant::DICTIONARY:
- case Variant::PACKED_BYTE_ARRAY:
- case Variant::PACKED_COLOR_ARRAY:
- case Variant::PACKED_INT32_ARRAY:
- case Variant::PACKED_INT64_ARRAY:
- case Variant::PACKED_FLOAT32_ARRAY:
- case Variant::PACKED_FLOAT64_ARRAY:
- case Variant::PACKED_STRING_ARRAY:
- case Variant::PACKED_VECTOR2_ARRAY:
- case Variant::PACKED_VECTOR3_ARRAY: {
- break;
- }
- default: {
- Callable::CallError err;
- Variant temp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
-
- bool valid = false;
- Variant res = temp.get(cn->value, &valid);
-
- if (valid) {
- node_type = _type_from_variant(res);
- node_type.is_constant = false;
- } else if (check_types) {
- _set_error("Can't get index \"" + String(cn->value) + "\" on base \"" +
- base_type.to_string() + "\".",
- op->line);
- return DataType();
- }
- } break;
- }
- } else {
- _mark_line_as_unsafe(op->line);
- }
- } else if (!for_completion && (index_type.kind != DataType::BUILTIN || index_type.builtin_type != Variant::STRING)) {
- _set_error("Only strings can be used as an index in the base type \"" + base_type.to_string() + "\".", op->line);
- return DataType();
- }
- }
- if (check_types && !node_type.has_type && base_type.kind == DataType::BUILTIN) {
- // Can infer indexing type for some variant types
- DataType result;
- result.has_type = true;
- result.kind = DataType::BUILTIN;
- switch (base_type.builtin_type) {
- // Can't index at all
- case Variant::NIL:
- case Variant::BOOL:
- case Variant::INT:
- case Variant::FLOAT:
- case Variant::NODE_PATH:
- case Variant::_RID: {
- _set_error("Can't index on a value of type \"" + base_type.to_string() + "\".", op->line);
- return DataType();
- } break;
- // Return int
- case Variant::PACKED_BYTE_ARRAY:
- case Variant::PACKED_INT32_ARRAY:
- case Variant::PACKED_INT64_ARRAY: {
- result.builtin_type = Variant::INT;
- } break;
- // Return real
- case Variant::PACKED_FLOAT32_ARRAY:
- case Variant::PACKED_FLOAT64_ARRAY:
- case Variant::VECTOR2:
- case Variant::VECTOR3:
- case Variant::QUAT: {
- result.builtin_type = Variant::FLOAT;
- } break;
- // Return color
- case Variant::PACKED_COLOR_ARRAY: {
- result.builtin_type = Variant::COLOR;
- } break;
- // Return string
- case Variant::PACKED_STRING_ARRAY:
- case Variant::STRING: {
- result.builtin_type = Variant::STRING;
- } break;
- // Return Vector2
- case Variant::PACKED_VECTOR2_ARRAY:
- case Variant::TRANSFORM2D:
- case Variant::RECT2: {
- result.builtin_type = Variant::VECTOR2;
- } break;
- // Return Vector3
- case Variant::PACKED_VECTOR3_ARRAY:
- case Variant::AABB:
- case Variant::BASIS: {
- result.builtin_type = Variant::VECTOR3;
- } break;
- // Depends on the index
- case Variant::TRANSFORM:
- case Variant::PLANE:
- case Variant::COLOR:
- default: {
- result.has_type = false;
- } break;
- }
- node_type = result;
- }
- } break;
- default: {
- _set_error("Parser bug: unhandled operation.", op->line);
- ERR_FAIL_V(DataType());
- }
+ if (key != nullptr && value != nullptr) {
+ dictionary->elements.push_back({ key, value });
}
- } break;
- default: {
- }
+ } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
}
+ pop_multiline();
+ consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" after dictionary elements.)");
- node_type = _resolve_type(node_type, p_node->line);
- p_node->set_datatype(node_type);
- return node_type;
+ return dictionary;
}
-bool GDScriptParser::_get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const {
- r_static = false;
- r_default_arg_count = 0;
-
- DataType original_type = p_base_type;
- ClassNode *base = nullptr;
- FunctionNode *callee = nullptr;
-
- if (p_base_type.kind == DataType::CLASS) {
- base = p_base_type.class_type;
+GDScriptParser::ExpressionNode *GDScriptParser::parse_grouping(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ ExpressionNode *grouped = parse_expression(false);
+ pop_multiline();
+ if (grouped == nullptr) {
+ push_error(R"(Expected grouping expression.)");
+ } else {
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after grouping expression.)*");
}
+ return grouped;
+}
- // Look up the current file (parse tree)
- while (!callee && base) {
- for (int i = 0; i < base->static_functions.size(); i++) {
- FunctionNode *func = base->static_functions[i];
- if (p_function == func->name) {
- r_static = true;
- callee = func;
- break;
- }
- }
- if (!callee && !p_base_type.is_meta_type) {
- for (int i = 0; i < base->functions.size(); i++) {
- FunctionNode *func = base->functions[i];
- if (p_function == func->name) {
- callee = func;
- break;
- }
+GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ SubscriptNode *attribute = alloc_node<SubscriptNode>();
+
+ if (for_completion) {
+ bool is_builtin = false;
+ if (p_previous_operand->type == Node::IDENTIFIER) {
+ const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand);
+ Variant::Type builtin_type = get_builtin_type(id->name);
+ if (builtin_type < Variant::VARIANT_MAX) {
+ make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT, builtin_type, true);
+ is_builtin = true;
}
}
- p_base_type = base->base_type;
- if (p_base_type.kind == DataType::CLASS) {
- base = p_base_type.class_type;
- } else {
- break;
+ if (!is_builtin) {
+ make_completion_context(COMPLETION_ATTRIBUTE, attribute, -1, true);
}
}
- if (callee) {
- r_return_type = callee->get_datatype();
- for (int i = 0; i < callee->argument_types.size(); i++) {
- r_arg_types.push_back(callee->argument_types[i]);
- }
- r_default_arg_count = callee->default_values.size();
- return true;
- }
+ attribute->is_attribute = true;
+ attribute->base = p_previous_operand;
- // Nothing in current file, check parent script
- Ref<GDScript> base_gdscript;
- Ref<Script> base_script;
- StringName native;
- if (p_base_type.kind == DataType::GDSCRIPT) {
- base_gdscript = p_base_type.script_type;
- if (base_gdscript.is_null() || !base_gdscript->is_valid()) {
- // GDScript wasn't properly compíled, don't bother trying
- return false;
- }
- } else if (p_base_type.kind == DataType::SCRIPT) {
- base_script = p_base_type.script_type;
- } else if (p_base_type.kind == DataType::NATIVE) {
- native = p_base_type.native_type;
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) {
+ return attribute;
}
+ attribute->attribute = parse_identifier();
- while (base_gdscript.is_valid()) {
- native = base_gdscript->get_instance_base_type();
+ return attribute;
+}
- Map<StringName, GDScriptFunction *> funcs = base_gdscript->get_member_functions();
+GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ SubscriptNode *subscript = alloc_node<SubscriptNode>();
- if (funcs.has(p_function)) {
- GDScriptFunction *f = funcs[p_function];
- r_static = f->is_static();
- r_default_arg_count = f->get_default_argument_count();
- r_return_type = _type_from_gdtype(f->get_return_type());
- for (int i = 0; i < f->get_argument_count(); i++) {
- r_arg_types.push_back(_type_from_gdtype(f->get_argument_type(i)));
- }
- return true;
- }
+ make_completion_context(COMPLETION_SUBSCRIPT, subscript);
- base_gdscript = base_gdscript->get_base_script();
- }
+ subscript->base = p_previous_operand;
+ subscript->index = parse_expression(false);
- while (base_script.is_valid()) {
- native = base_script->get_instance_base_type();
- MethodInfo mi = base_script->get_method_info(p_function);
+ pop_multiline();
+ consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)");
- if (!(mi == MethodInfo())) {
- r_return_type = _type_from_property(mi.return_val, false);
- r_default_arg_count = mi.default_arguments.size();
- for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
- r_arg_types.push_back(_type_from_property(E->get()));
- }
- return true;
- }
- base_script = base_script->get_base_script();
- }
+ return subscript;
+}
- if (native == StringName()) {
- // Empty native class, might happen in some Script implementations
- // Just ignore it
- return false;
- }
+GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ CastNode *cast = alloc_node<CastNode>();
-#ifdef DEBUG_METHODS_ENABLED
+ cast->operand = p_previous_operand;
+ cast->cast_type = parse_type();
- // Only native remains
- if (!ClassDB::class_exists(native)) {
- native = "_" + native.operator String();
- }
- if (!ClassDB::class_exists(native)) {
- if (!check_types) {
- return false;
- }
- ERR_FAIL_V_MSG(false, "Parser bug: Class '" + String(native) + "' not found.");
+ if (cast->cast_type == nullptr) {
+ push_error(R"(Expected type specifier after "as".)");
+ return p_previous_operand;
}
- MethodBind *method = ClassDB::get_method(native, p_function);
+ return cast;
+}
- if (!method) {
- // Try virtual methods
- List<MethodInfo> virtuals;
- ClassDB::get_virtual_methods(native, &virtuals);
+GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ CallNode *call = alloc_node<CallNode>();
- for (const List<MethodInfo>::Element *E = virtuals.front(); E; E = E->next()) {
- const MethodInfo &mi = E->get();
- if (mi.name == p_function) {
- r_default_arg_count = mi.default_arguments.size();
- for (const List<PropertyInfo>::Element *pi = mi.arguments.front(); pi; pi = pi->next()) {
- r_arg_types.push_back(_type_from_property(pi->get()));
- }
- r_return_type = _type_from_property(mi.return_val, false);
- r_vararg = mi.flags & METHOD_FLAG_VARARG;
- return true;
+ if (previous.type == GDScriptTokenizer::Token::SUPER) {
+ // Super call.
+ call->is_super = true;
+ push_multiline(true);
+ if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
+ // Implicit call to the parent method of the same name.
+ if (current_function == nullptr) {
+ push_error(R"(Cannot use implicit "super" call outside of a function.)");
+ pop_multiline();
+ return nullptr;
}
+ call->function_name = current_function->identifier->name;
+ } else {
+ consume(GDScriptTokenizer::Token::PERIOD, R"(Expected "." or "(" after "super".)");
+ make_completion_context(COMPLETION_SUPER_METHOD, call, true);
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) {
+ pop_multiline();
+ return nullptr;
+ }
+ IdentifierNode *identifier = parse_identifier();
+ call->callee = identifier;
+ call->function_name = identifier->name;
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after function name.)");
}
-
- // If the base is a script, it might be trying to access members of the Script class itself
- if (original_type.is_meta_type && !(p_function == "new") && (original_type.kind == DataType::SCRIPT || original_type.kind == DataType::GDSCRIPT)) {
- method = ClassDB::get_method(original_type.script_type->get_class_name(), p_function);
-
- if (method) {
- r_static = true;
+ } else {
+ call->callee = p_previous_operand;
+
+ if (call->callee == nullptr) {
+ push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
+ } else if (call->callee->type == Node::IDENTIFIER) {
+ call->function_name = static_cast<IdentifierNode *>(call->callee)->name;
+ make_completion_context(COMPLETION_METHOD, call->callee);
+ } else if (call->callee->type == Node::SUBSCRIPT) {
+ SubscriptNode *attribute = static_cast<SubscriptNode *>(call->callee);
+ if (attribute->is_attribute) {
+ if (attribute->attribute) {
+ call->function_name = attribute->attribute->name;
+ }
+ make_completion_context(COMPLETION_ATTRIBUTE_METHOD, call->callee);
} else {
- // Try virtual methods of the script type
- virtuals.clear();
- ClassDB::get_virtual_methods(original_type.script_type->get_class_name(), &virtuals);
- for (const List<MethodInfo>::Element *E = virtuals.front(); E; E = E->next()) {
- const MethodInfo &mi = E->get();
- if (mi.name == p_function) {
- r_default_arg_count = mi.default_arguments.size();
- for (const List<PropertyInfo>::Element *pi = mi.arguments.front(); pi; pi = pi->next()) {
- r_arg_types.push_back(_type_from_property(pi->get()));
- }
- r_return_type = _type_from_property(mi.return_val, false);
- r_static = true;
- r_vararg = mi.flags & METHOD_FLAG_VARARG;
- return true;
- }
- }
- return false;
+ // TODO: The analyzer can see if this is actually a Callable and give better error message.
+ push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
}
} else {
- return false;
+ push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
}
}
- r_default_arg_count = method->get_default_argument_count();
- r_return_type = _type_from_property(method->get_return_info(), false);
- r_vararg = method->is_vararg();
-
- for (int i = 0; i < method->get_argument_count(); i++) {
- r_arg_types.push_back(_type_from_property(method->get_argument_info(i)));
- }
- return true;
-#else
- return false;
-#endif
-}
-
-GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const OperatorNode *p_call) {
- if (p_call->arguments.size() < 1) {
- _set_error("Parser bug: function call without enough arguments.", p_call->line);
- ERR_FAIL_V(DataType());
- }
-
- DataType return_type;
- List<DataType> arg_types;
- int default_args_count = 0;
- int arg_count = p_call->arguments.size();
- String callee_name;
- bool is_vararg = false;
-
- switch (p_call->arguments[0]->type) {
- case GDScriptParser::Node::TYPE_TYPE: {
- // Built-in constructor, special case
- TypeNode *tn = static_cast<TypeNode *>(p_call->arguments[0]);
-
- Vector<DataType> par_types;
- par_types.resize(p_call->arguments.size() - 1);
- for (int i = 1; i < p_call->arguments.size(); i++) {
- par_types.write[i - 1] = _reduce_node_type(p_call->arguments[i]);
- }
-
- if (error_set) {
- return DataType();
+ if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Arguments.
+ push_completion_call(call);
+ make_completion_context(COMPLETION_CALL_ARGUMENTS, call, 0, true);
+ int argument_index = 0;
+ do {
+ make_completion_context(COMPLETION_CALL_ARGUMENTS, call, argument_index++, true);
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Allow for trailing comma.
+ break;
}
-
- // Special case: check copy constructor. Those are defined implicitly in Variant.
- if (par_types.size() == 1) {
- if (!par_types[0].has_type || (par_types[0].kind == DataType::BUILTIN && par_types[0].builtin_type == tn->vtype)) {
- DataType result;
- result.has_type = true;
- result.kind = DataType::BUILTIN;
- result.builtin_type = tn->vtype;
- return result;
- }
+ ExpressionNode *argument = parse_expression(false);
+ if (argument == nullptr) {
+ push_error(R"(Expected expression as the function argument.)");
+ } else {
+ call->arguments.push_back(argument);
}
+ } while (match(GDScriptTokenizer::Token::COMMA));
+ pop_completion_call();
+ }
- bool match = false;
- List<MethodInfo> constructors;
- Variant::get_constructor_list(tn->vtype, &constructors);
- PropertyInfo return_type2;
-
- for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
- MethodInfo &mi = E->get();
-
- if (p_call->arguments.size() - 1 < mi.arguments.size() - mi.default_arguments.size()) {
- continue;
- }
- if (p_call->arguments.size() - 1 > mi.arguments.size()) {
- continue;
- }
-
- bool types_match = true;
- for (int i = 0; i < par_types.size(); i++) {
- DataType arg_type;
- if (mi.arguments[i].type != Variant::NIL) {
- arg_type.has_type = true;
- arg_type.kind = mi.arguments[i].type == Variant::OBJECT ? DataType::NATIVE : DataType::BUILTIN;
- arg_type.builtin_type = mi.arguments[i].type;
- arg_type.native_type = mi.arguments[i].class_name;
- }
+ pop_multiline();
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
- if (!_is_type_compatible(arg_type, par_types[i], true)) {
- types_match = false;
- break;
- } else {
-#ifdef DEBUG_ENABLED
- if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_types[i].kind == DataType::BUILTIN && par_types[i].builtin_type == Variant::FLOAT) {
- _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, Variant::get_type_name(tn->vtype));
- }
- if (par_types[i].may_yield && p_call->arguments[i + 1]->type == Node::TYPE_OPERATOR) {
- _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i + 1])));
- }
-#endif // DEBUG_ENABLED
- }
- }
+ return call;
+}
- if (types_match) {
- match = true;
- return_type2 = mi.return_val;
- break;
- }
+GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ if (match(GDScriptTokenizer::Token::LITERAL)) {
+ if (previous.literal.get_type() != Variant::STRING) {
+ push_error(R"(Expect node path as string or identifier after "$".)");
+ return nullptr;
+ }
+ GetNodeNode *get_node = alloc_node<GetNodeNode>();
+ make_completion_context(COMPLETION_GET_NODE, get_node);
+ get_node->string = parse_literal();
+ return get_node;
+ } else if (current.is_node_name()) {
+ GetNodeNode *get_node = alloc_node<GetNodeNode>();
+ int chain_position = 0;
+ do {
+ make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
+ if (!current.is_node_name()) {
+ push_error(R"(Expect node path after "/".)");
+ return nullptr;
}
+ advance();
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = previous.get_identifier();
+ get_node->chain.push_back(identifier);
+ } while (match(GDScriptTokenizer::Token::SLASH));
+ return get_node;
+ } else {
+ push_error(R"(Expect node path as string or identifier after "$".)");
+ return nullptr;
+ }
+}
- if (match) {
- return _type_from_property(return_type2, false);
- } else if (check_types) {
- String err = "No constructor of '";
- err += Variant::get_type_name(tn->vtype);
- err += "' matches the signature '";
- err += Variant::get_type_name(tn->vtype) + "(";
- for (int i = 0; i < par_types.size(); i++) {
- if (i > 0) {
- err += ", ";
- }
- err += par_types[i].to_string();
- }
- err += ")'.";
- _set_error(err, p_call->line, p_call->column);
- return DataType();
- }
- return DataType();
- } break;
- case GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION: {
- BuiltInFunctionNode *func = static_cast<BuiltInFunctionNode *>(p_call->arguments[0]);
- MethodInfo mi = GDScriptFunctions::get_info(func->function);
+GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ PreloadNode *preload = alloc_node<PreloadNode>();
+ preload->resolved_path = "<missing path>";
- return_type = _type_from_property(mi.return_val, false);
+ push_multiline(true);
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "preload".)");
- // Check all arguments beforehand to solve warnings
- for (int i = 1; i < p_call->arguments.size(); i++) {
- _reduce_node_type(p_call->arguments[i]);
- }
+ make_completion_context(COMPLETION_RESOURCE_PATH, preload);
+ push_completion_call(preload);
- // Check arguments
+ preload->path = parse_expression(false);
- is_vararg = mi.flags & METHOD_FLAG_VARARG;
+ if (preload->path == nullptr) {
+ push_error(R"(Expected resource path after "(".)");
+ }
- default_args_count = mi.default_arguments.size();
- callee_name = mi.name;
- arg_count -= 1;
+ pop_completion_call();
- // Check each argument type
- for (List<PropertyInfo>::Element *E = mi.arguments.front(); E; E = E->next()) {
- arg_types.push_back(_type_from_property(E->get()));
- }
- } break;
- default: {
- if (p_call->op == OperatorNode::OP_CALL && p_call->arguments.size() < 2) {
- _set_error("Parser bug: self method call without enough arguments.", p_call->line);
- ERR_FAIL_V(DataType());
- }
+ pop_multiline();
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after preload path.)*");
- int arg_id = p_call->op == OperatorNode::OP_CALL ? 1 : 0;
+ return preload;
+}
- if (p_call->arguments[arg_id]->type != Node::TYPE_IDENTIFIER) {
- _set_error("Parser bug: invalid function call argument.", p_call->line);
- ERR_FAIL_V(DataType());
- }
+GDScriptParser::ExpressionNode *GDScriptParser::parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ // Just for better error messages.
+ GDScriptTokenizer::Token::Type invalid = previous.type;
- // Check all arguments beforehand to solve warnings
- for (int i = arg_id + 1; i < p_call->arguments.size(); i++) {
- _reduce_node_type(p_call->arguments[i]);
- }
+ switch (invalid) {
+ case GDScriptTokenizer::Token::QUESTION_MARK:
+ push_error(R"(Unexpected "?" in source. If you want a ternary operator, use "truthy_value if true_condition else falsy_value".)");
+ break;
+ default:
+ return nullptr; // Unreachable.
+ }
- IdentifierNode *func_id = static_cast<IdentifierNode *>(p_call->arguments[arg_id]);
- callee_name = func_id->name;
- arg_count -= 1 + arg_id;
+ // Return the previous expression.
+ return p_previous_operand;
+}
- DataType base_type;
- if (p_call->op == OperatorNode::OP_PARENT_CALL) {
- base_type = current_class->base_type;
+GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
+ TypeNode *type = alloc_node<TypeNode>();
+ make_completion_context(p_allow_void ? COMPLETION_TYPE_NAME_OR_VOID : COMPLETION_TYPE_NAME, type);
+ if (!match(GDScriptTokenizer::Token::IDENTIFIER)) {
+ if (match(GDScriptTokenizer::Token::VOID)) {
+ if (p_allow_void) {
+ TypeNode *void_type = alloc_node<TypeNode>();
+ return void_type;
} else {
- base_type = _reduce_node_type(p_call->arguments[0]);
- }
-
- if (!base_type.has_type || (base_type.kind == DataType::BUILTIN && base_type.builtin_type == Variant::NIL)) {
- _mark_line_as_unsafe(p_call->line);
- return DataType();
+ push_error(R"("void" is only allowed for a function return type.)");
}
+ }
+ // Leave error message to the caller who knows the context.
+ return nullptr;
+ }
- if (base_type.kind == DataType::BUILTIN) {
- Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
-
- if (check_types) {
- if (!tmp.has_method(callee_name)) {
- _set_error("The method \"" + callee_name + "\" isn't declared on base \"" + base_type.to_string() + "\".", p_call->line);
- return DataType();
- }
-
- default_args_count = Variant::get_method_default_arguments(base_type.builtin_type, callee_name).size();
- const Vector<Variant::Type> &var_arg_types = Variant::get_method_argument_types(base_type.builtin_type, callee_name);
-
- for (int i = 0; i < var_arg_types.size(); i++) {
- DataType argtype;
- if (var_arg_types[i] != Variant::NIL) {
- argtype.has_type = true;
- argtype.kind = DataType::BUILTIN;
- argtype.builtin_type = var_arg_types[i];
- }
- arg_types.push_back(argtype);
- }
- }
+ IdentifierNode *type_element = parse_identifier();
+
+ type->type_chain.push_back(type_element);
+
+ int chain_index = 1;
+ while (match(GDScriptTokenizer::Token::PERIOD)) {
+ make_completion_context(COMPLETION_TYPE_ATTRIBUTE, type, chain_index++);
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected inner type name after ".".)")) {
+ type_element = parse_identifier();
+ type->type_chain.push_back(type_element);
+ }
+ }
+
+ return type;
+}
+
+GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Type p_token_type) {
+ // Function table for expression parsing.
+ // clang-format destroys the alignment here, so turn off for the table.
+ /* clang-format off */
+ static ParseRule rules[] = {
+ // PREFIX INFIX PRECEDENCE (for infix)
+ { nullptr, nullptr, PREC_NONE }, // EMPTY,
+ // Basic
+ { nullptr, nullptr, PREC_NONE }, // ANNOTATION,
+ { &GDScriptParser::parse_identifier, nullptr, PREC_NONE }, // IDENTIFIER,
+ { &GDScriptParser::parse_literal, nullptr, PREC_NONE }, // LITERAL,
+ // Comparison
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // LESS,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // LESS_EQUAL,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // GREATER,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // GREATER_EQUAL,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // EQUAL_EQUAL,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_COMPARISON }, // BANG_EQUAL,
+ // Logical
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AND,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // OR,
+ { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // NOT,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AMPERSAND_AMPERSAND,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // PIPE_PIPE,
+ { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // BANG,
+ // Bitwise
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_AND }, // AMPERSAND,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_OR }, // PIPE,
+ { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // TILDE,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_XOR }, // CARET,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // LESS_LESS,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // GREATER_GREATER,
+ // Math
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION }, // PLUS,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_SUBTRACTION }, // MINUS,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
+ // Assignment
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // MINUS_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // SLASH_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PERCENT_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // LESS_LESS_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // GREATER_GREATER_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // AMPERSAND_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PIPE_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // CARET_EQUAL,
+ // Control flow
+ { nullptr, &GDScriptParser::parse_ternary_operator, PREC_TERNARY }, // IF,
+ { nullptr, nullptr, PREC_NONE }, // ELIF,
+ { nullptr, nullptr, PREC_NONE }, // ELSE,
+ { nullptr, nullptr, PREC_NONE }, // FOR,
+ { nullptr, nullptr, PREC_NONE }, // WHILE,
+ { nullptr, nullptr, PREC_NONE }, // BREAK,
+ { nullptr, nullptr, PREC_NONE }, // CONTINUE,
+ { nullptr, nullptr, PREC_NONE }, // PASS,
+ { nullptr, nullptr, PREC_NONE }, // RETURN,
+ { nullptr, nullptr, PREC_NONE }, // MATCH,
+ // Keywords
+ { nullptr, &GDScriptParser::parse_cast, PREC_CAST }, // AS,
+ { nullptr, nullptr, PREC_NONE }, // ASSERT,
+ { &GDScriptParser::parse_await, nullptr, PREC_NONE }, // AWAIT,
+ { nullptr, nullptr, PREC_NONE }, // BREAKPOINT,
+ { nullptr, nullptr, PREC_NONE }, // CLASS,
+ { nullptr, nullptr, PREC_NONE }, // CLASS_NAME,
+ { nullptr, nullptr, PREC_NONE }, // CONST,
+ { nullptr, nullptr, PREC_NONE }, // ENUM,
+ { nullptr, nullptr, PREC_NONE }, // EXTENDS,
+ { nullptr, nullptr, PREC_NONE }, // FUNC,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_CONTENT_TEST }, // IN,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_TYPE_TEST }, // IS,
+ { nullptr, nullptr, PREC_NONE }, // NAMESPACE,
+ { &GDScriptParser::parse_preload, nullptr, PREC_NONE }, // PRELOAD,
+ { &GDScriptParser::parse_self, nullptr, PREC_NONE }, // SELF,
+ { nullptr, nullptr, PREC_NONE }, // SIGNAL,
+ { nullptr, nullptr, PREC_NONE }, // STATIC,
+ { &GDScriptParser::parse_call, nullptr, PREC_NONE }, // SUPER,
+ { nullptr, nullptr, PREC_NONE }, // TRAIT,
+ { nullptr, nullptr, PREC_NONE }, // VAR,
+ { nullptr, nullptr, PREC_NONE }, // VOID,
+ { nullptr, nullptr, PREC_NONE }, // YIELD,
+ // Punctuation
+ { &GDScriptParser::parse_array, &GDScriptParser::parse_subscript, PREC_SUBSCRIPT }, // BRACKET_OPEN,
+ { nullptr, nullptr, PREC_NONE }, // BRACKET_CLOSE,
+ { &GDScriptParser::parse_dictionary, nullptr, PREC_NONE }, // BRACE_OPEN,
+ { nullptr, nullptr, PREC_NONE }, // BRACE_CLOSE,
+ { &GDScriptParser::parse_grouping, &GDScriptParser::parse_call, PREC_CALL }, // PARENTHESIS_OPEN,
+ { nullptr, nullptr, PREC_NONE }, // PARENTHESIS_CLOSE,
+ { nullptr, nullptr, PREC_NONE }, // COMMA,
+ { nullptr, nullptr, PREC_NONE }, // SEMICOLON,
+ { nullptr, &GDScriptParser::parse_attribute, PREC_ATTRIBUTE }, // PERIOD,
+ { nullptr, nullptr, PREC_NONE }, // PERIOD_PERIOD,
+ { nullptr, nullptr, PREC_NONE }, // COLON,
+ { &GDScriptParser::parse_get_node, nullptr, PREC_NONE }, // DOLLAR,
+ { nullptr, nullptr, PREC_NONE }, // FORWARD_ARROW,
+ { nullptr, nullptr, PREC_NONE }, // UNDERSCORE,
+ // Whitespace
+ { nullptr, nullptr, PREC_NONE }, // NEWLINE,
+ { nullptr, nullptr, PREC_NONE }, // INDENT,
+ { nullptr, nullptr, PREC_NONE }, // DEDENT,
+ // Constants
+ { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_PI,
+ { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_TAU,
+ { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_INF,
+ { &GDScriptParser::parse_builtin_constant, nullptr, PREC_NONE }, // CONST_NAN,
+ // Error message improvement
+ { nullptr, nullptr, PREC_NONE }, // VCS_CONFLICT_MARKER,
+ { nullptr, nullptr, PREC_NONE }, // BACKTICK,
+ { nullptr, &GDScriptParser::parse_invalid_token, PREC_CAST }, // QUESTION_MARK,
+ // Special
+ { nullptr, nullptr, PREC_NONE }, // ERROR,
+ { nullptr, nullptr, PREC_NONE }, // TK_EOF,
+ };
+ /* clang-format on */
+ // Avoid desync.
+ static_assert(sizeof(rules) / sizeof(rules[0]) == GDScriptTokenizer::Token::TK_MAX, "Amount of parse rules don't match the amount of token types.");
+
+ // Let's assume this this never invalid, since nothing generates a TK_MAX.
+ return &rules[p_token_type];
+}
+
+bool GDScriptParser::SuiteNode::has_local(const StringName &p_name) const {
+ if (locals_indices.has(p_name)) {
+ return true;
+ }
+ if (parent_block != nullptr) {
+ return parent_block->has_local(p_name);
+ }
+ return false;
+}
- bool rets = false;
- return_type.has_type = true;
- return_type.kind = DataType::BUILTIN;
- return_type.builtin_type = Variant::get_method_return_type(base_type.builtin_type, callee_name, &rets);
- // If the method returns, but it might return any type, (Variant::NIL), pretend we don't know the type.
- // At least make sure we know that it returns
- if (rets && return_type.builtin_type == Variant::NIL) {
- return_type.has_type = false;
- }
- break;
- }
+const GDScriptParser::SuiteNode::Local &GDScriptParser::SuiteNode::get_local(const StringName &p_name) const {
+ if (locals_indices.has(p_name)) {
+ return locals[locals_indices[p_name]];
+ }
+ if (parent_block != nullptr) {
+ return parent_block->get_local(p_name);
+ }
+ return empty;
+}
- DataType original_type = base_type;
- bool is_initializer = callee_name == "new";
- bool is_static = false;
- bool valid = false;
+bool GDScriptParser::AnnotationNode::apply(GDScriptParser *p_this, Node *p_target) const {
+ return (p_this->*(p_this->valid_annotations[name].apply))(this, p_target);
+}
- if (is_initializer && original_type.is_meta_type) {
- // Try to check it as initializer
- base_type = original_type;
- callee_name = "_init";
- base_type.is_meta_type = false;
+bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const {
+ return (info->target_kind & p_target_kinds) > 0;
+}
- valid = _get_function_signature(base_type, callee_name, return_type, arg_types,
- default_args_count, is_static, is_vararg);
+bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) {
+ ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name));
- return_type = original_type;
- return_type.is_meta_type = false;
+ const MethodInfo &info = valid_annotations[p_annotation->name].info;
- valid = true; // There's always an initializer, we can assume this is true
- }
+ if (((info.flags & METHOD_FLAG_VARARG) == 0) && p_annotation->arguments.size() > info.arguments.size()) {
+ push_error(vformat(R"(Annotation "%s" requires at most %d arguments, but %d were given.)", p_annotation->name, info.arguments.size(), p_annotation->arguments.size()));
+ return false;
+ }
- if (!valid) {
- base_type = original_type;
- return_type = DataType();
- valid = _get_function_signature(base_type, callee_name, return_type, arg_types,
- default_args_count, is_static, is_vararg);
- }
+ if (p_annotation->arguments.size() < info.arguments.size() - info.default_arguments.size()) {
+ push_error(vformat(R"(Annotation "%s" requires at least %d arguments, but %d were given.)", p_annotation->name, info.arguments.size() - info.default_arguments.size(), p_annotation->arguments.size()));
+ return false;
+ }
- if (!valid) {
-#ifdef DEBUG_ENABLED
- if (p_call->arguments[0]->type == Node::TYPE_SELF) {
- _set_error("The method \"" + callee_name + "\" isn't declared in the current class.", p_call->line);
- return DataType();
- }
- DataType tmp_type;
- valid = _get_member_type(original_type, func_id->name, tmp_type);
- if (valid) {
- if (tmp_type.is_constant) {
- _add_warning(GDScriptWarning::CONSTANT_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string());
- } else {
- _add_warning(GDScriptWarning::PROPERTY_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string());
+ const List<PropertyInfo>::Element *E = info.arguments.front();
+ for (int i = 0; i < p_annotation->arguments.size(); i++) {
+ ExpressionNode *argument = p_annotation->arguments[i];
+ const PropertyInfo &parameter = E->get();
+
+ if (E->next() != nullptr) {
+ E = E->next();
+ }
+
+ switch (parameter.type) {
+ case Variant::STRING:
+ case Variant::STRING_NAME:
+ case Variant::NODE_PATH:
+ // Allow "quote-less strings", as long as they are recognized as identifiers.
+ if (argument->type == Node::IDENTIFIER) {
+ IdentifierNode *string = static_cast<IdentifierNode *>(argument);
+ Callable::CallError error;
+ Vector<Variant> args = varray(string->name);
+ const Variant *name = args.ptr();
+ p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(name), 1, error));
+ if (error.error != Callable::CallError::CALL_OK) {
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
+ return false;
}
+ break;
+ }
+ [[fallthrough]];
+ default: {
+ if (argument->type != Node::LITERAL) {
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ return false;
}
- _add_warning(GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->line, callee_name, original_type.to_string());
- _mark_line_as_unsafe(p_call->line);
-#endif // DEBUG_ENABLED
- return DataType();
- }
-
-#ifdef DEBUG_ENABLED
- if (current_function && !for_completion && !is_static && p_call->arguments[0]->type == Node::TYPE_SELF && current_function->_static) {
- _set_error("Can't call non-static function from a static function.", p_call->line);
- return DataType();
- }
-
- if (check_types && !is_static && !is_initializer && base_type.is_meta_type) {
- _set_error("Non-static function \"" + String(callee_name) + "\" can only be called from an instance.", p_call->line);
- return DataType();
- }
- // Check signal emission for warnings
- if (callee_name == "emit_signal" && p_call->op == OperatorNode::OP_CALL && p_call->arguments[0]->type == Node::TYPE_SELF && p_call->arguments.size() >= 3 && p_call->arguments[2]->type == Node::TYPE_CONSTANT) {
- ConstantNode *sig = static_cast<ConstantNode *>(p_call->arguments[2]);
- String emitted = sig->value.get_type() == Variant::STRING ? sig->value.operator String() : "";
- for (int i = 0; i < current_class->_signals.size(); i++) {
- if (current_class->_signals[i].name == emitted) {
- current_class->_signals.write[i].emissions += 1;
- break;
- }
+ Variant value = static_cast<LiteralNode *>(argument)->value;
+ if (!Variant::can_convert_strict(value.get_type(), parameter.type)) {
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ return false;
}
+ Callable::CallError error;
+ const Variant *args = &value;
+ p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(args), 1, error));
+ if (error.error != Callable::CallError::CALL_OK) {
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
+ return false;
+ }
+ break;
}
-#endif // DEBUG_ENABLED
- } break;
- }
-
-#ifdef DEBUG_ENABLED
- if (!check_types) {
- return return_type;
+ }
}
- if (arg_count < arg_types.size() - default_args_count) {
- _set_error("Too few arguments for \"" + callee_name + "()\" call. Expected at least " + itos(arg_types.size() - default_args_count) + ".", p_call->line);
- return return_type;
- }
- if (!is_vararg && arg_count > arg_types.size()) {
- _set_error("Too many arguments for \"" + callee_name + "()\" call. Expected at most " + itos(arg_types.size()) + ".", p_call->line);
- return return_type;
- }
+ return true;
+}
- int arg_diff = p_call->arguments.size() - arg_count;
- for (int i = arg_diff; i < p_call->arguments.size(); i++) {
- DataType par_type = _reduce_node_type(p_call->arguments[i]);
+bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p_node) {
+ this->_is_tool = true;
+ return true;
+}
- if ((i - arg_diff) >= arg_types.size()) {
- continue;
- }
+bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p_node) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)");
+ ClassNode *p_class = static_cast<ClassNode *>(p_node);
+ p_class->icon_path = p_annotation->resolved_arguments[0];
+ return true;
+}
- DataType arg_type = arg_types[i - arg_diff];
+bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node *p_node) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)");
- if (!par_type.has_type) {
- _mark_line_as_unsafe(p_call->line);
- if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) {
- _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i])));
- }
- } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) {
- // Supertypes are acceptable for dynamic compliance
- if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) {
- _set_error("At \"" + callee_name + "()\" call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" +
- par_type.to_string() + ") doesn't match the function argument's type (" +
- arg_types[i - arg_diff].to_string() + ").",
- p_call->line);
- return DataType();
- } else {
- _mark_line_as_unsafe(p_call->line);
- }
- } else {
- if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_type.kind == DataType::BUILTIN && par_type.builtin_type == Variant::FLOAT) {
- _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, callee_name);
- }
- }
+ VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->onready) {
+ push_error(R"("@onready" annotation can only be used once per variable.)");
+ return false;
}
-
-#endif // DEBUG_ENABLED
-
- return return_type;
+ variable->onready = true;
+ current_class->onready_used = true;
+ return true;
}
-bool GDScriptParser::_get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type, bool *r_is_const) const {
- DataType base_type = p_base_type;
+template <PropertyHint t_hint, Variant::Type t_type>
+bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_node) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
- // Check classes in current file
- ClassNode *base = nullptr;
- if (base_type.kind == DataType::CLASS) {
- base = base_type.class_type;
+ VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->exported) {
+ push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
+ return false;
}
- while (base) {
- if (base->constant_expressions.has(p_member)) {
- if (r_is_const) {
- *r_is_const = true;
- }
- r_member_type = base->constant_expressions[p_member].expression->get_datatype();
- return true;
- }
+ variable->exported = true;
+ // TODO: Improving setting type, especially for range hints, which can be int or float.
+ variable->export_info.type = t_type;
+ variable->export_info.hint = t_hint;
- if (!base_type.is_meta_type) {
- for (int i = 0; i < base->variables.size(); i++) {
- if (base->variables[i].identifier == p_member) {
- r_member_type = base->variables[i].data_type;
- base->variables.write[i].usages += 1;
- return true;
- }
+ if (p_annotation->name == "@export") {
+ if (variable->datatype_specifier == nullptr) {
+ if (variable->initializer == nullptr) {
+ push_error(R"(Cannot use "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
+ return false;
}
- } else {
- for (int i = 0; i < base->subclasses.size(); i++) {
- ClassNode *c = base->subclasses[i];
- if (c->name == p_member) {
- DataType class_type;
- class_type.has_type = true;
- class_type.is_constant = true;
- class_type.is_meta_type = true;
- class_type.kind = DataType::CLASS;
- class_type.class_type = c;
- r_member_type = class_type;
- return true;
- }
+ if (variable->initializer->type != Node::LITERAL) {
+ push_error(R"(To use "@export" annotation with type-less variable, the default value must be a literal.)", p_annotation);
+ return false;
}
- }
-
- base_type = base->base_type;
- if (base_type.kind == DataType::CLASS) {
- base = base_type.class_type;
- } else {
- break;
- }
+ variable->export_info.type = static_cast<LiteralNode *>(variable->initializer)->value.get_type();
+ } // else: Actual type will be set by the analyzer, which can infer the proper type.
}
- Ref<GDScript> gds;
- if (base_type.kind == DataType::GDSCRIPT) {
- gds = base_type.script_type;
- if (gds.is_null() || !gds->is_valid()) {
- // GDScript wasn't properly compíled, don't bother trying
- return false;
+ String hint_string;
+ for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
+ if (i > 0) {
+ hint_string += ",";
}
+ hint_string += String(p_annotation->resolved_arguments[i]);
}
- Ref<Script> scr;
- if (base_type.kind == DataType::SCRIPT) {
- scr = base_type.script_type;
- }
-
- StringName native;
- if (base_type.kind == DataType::NATIVE) {
- native = base_type.native_type;
- }
-
- // Check GDScripts
- while (gds.is_valid()) {
- if (gds->get_constants().has(p_member)) {
- Variant c = gds->get_constants()[p_member];
- r_member_type = _type_from_variant(c);
- return true;
- }
-
- if (!base_type.is_meta_type) {
- if (gds->get_members().has(p_member)) {
- r_member_type = _type_from_gdtype(gds->get_member_type(p_member));
- return true;
- }
- }
+ variable->export_info.hint_string = hint_string;
- native = gds->get_instance_base_type();
- if (gds->get_base_script().is_valid()) {
- gds = gds->get_base_script();
- scr = gds->get_base_script();
- bool is_meta = base_type.is_meta_type;
- base_type = _type_from_variant(scr.operator Variant());
- base_type.is_meta_type = is_meta;
- } else {
- break;
- }
- }
+ return true;
+}
-#define IS_USAGE_MEMBER(m_usage) (!(m_usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_CATEGORY)))
+bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
+ ERR_FAIL_V_MSG(false, "Not implemented.");
+}
- // Check other script types
- while (scr.is_valid()) {
- Map<StringName, Variant> constants;
- scr->get_constants(&constants);
- if (constants.has(p_member)) {
- r_member_type = _type_from_variant(constants[p_member]);
- return true;
- }
+template <MultiplayerAPI::RPCMode t_mode>
+bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Node *p_node) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE && p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to variables and functions.)", p_annotation->name));
- List<PropertyInfo> properties;
- scr->get_script_property_list(&properties);
- for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().name == p_member && IS_USAGE_MEMBER(E->get().usage)) {
- r_member_type = _type_from_property(E->get());
- return true;
+ switch (p_node->type) {
+ case Node::VARIABLE: {
+ VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
+ push_error(R"(RPC annotations can only be used once per variable.)", p_annotation);
}
+ variable->rpc_mode = t_mode;
+ break;
}
-
- base_type = _type_from_variant(scr.operator Variant());
- native = scr->get_instance_base_type();
- scr = scr->get_base_script();
- }
-
- if (native == StringName()) {
- // Empty native class, might happen in some Script implementations
- // Just ignore it
- return false;
- }
-
- // Check ClassDB
- if (!ClassDB::class_exists(native)) {
- native = "_" + native.operator String();
- }
- if (!ClassDB::class_exists(native)) {
- if (!check_types) {
- return false;
- }
- ERR_FAIL_V_MSG(false, "Parser bug: Class \"" + String(native) + "\" not found.");
- }
-
- bool valid = false;
- ClassDB::get_integer_constant(native, p_member, &valid);
- if (valid) {
- DataType ct;
- ct.has_type = true;
- ct.is_constant = true;
- ct.kind = DataType::BUILTIN;
- ct.builtin_type = Variant::INT;
- r_member_type = ct;
- return true;
- }
-
- if (!base_type.is_meta_type) {
- List<PropertyInfo> properties;
- ClassDB::get_property_list(native, &properties);
- for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().name == p_member && IS_USAGE_MEMBER(E->get().usage)) {
- // Check if a getter exists
- StringName getter_name = ClassDB::get_property_getter(native, p_member);
- if (getter_name != StringName()) {
- // Use the getter return type
-#ifdef DEBUG_METHODS_ENABLED
- MethodBind *getter_method = ClassDB::get_method(native, getter_name);
- if (getter_method) {
- r_member_type = _type_from_property(getter_method->get_return_info());
- } else {
- r_member_type = DataType();
- }
-#else
- r_member_type = DataType();
-#endif
- } else {
- r_member_type = _type_from_property(E->get());
- }
- return true;
- }
- }
- }
-
- // If the base is a script, it might be trying to access members of the Script class itself
- if (p_base_type.is_meta_type && (p_base_type.kind == DataType::SCRIPT || p_base_type.kind == DataType::GDSCRIPT)) {
- native = p_base_type.script_type->get_class_name();
- ClassDB::get_integer_constant(native, p_member, &valid);
- if (valid) {
- DataType ct;
- ct.has_type = true;
- ct.is_constant = true;
- ct.kind = DataType::BUILTIN;
- ct.builtin_type = Variant::INT;
- r_member_type = ct;
- return true;
- }
-
- List<PropertyInfo> properties;
- ClassDB::get_property_list(native, &properties);
- for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().name == p_member && IS_USAGE_MEMBER(E->get().usage)) {
- // Check if a getter exists
- StringName getter_name = ClassDB::get_property_getter(native, p_member);
- if (getter_name != StringName()) {
- // Use the getter return type
-#ifdef DEBUG_METHODS_ENABLED
- MethodBind *getter_method = ClassDB::get_method(native, getter_name);
- if (getter_method) {
- r_member_type = _type_from_property(getter_method->get_return_info());
- } else {
- r_member_type = DataType();
- }
-#else
- r_member_type = DataType();
-#endif
- } else {
- r_member_type = _type_from_property(E->get());
- }
- return true;
+ case Node::FUNCTION: {
+ FunctionNode *function = static_cast<FunctionNode *>(p_node);
+ if (function->rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
+ push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
}
+ function->rpc_mode = t_mode;
+ break;
}
+ default:
+ return false; // Unreachable.
}
-#undef IS_USAGE_MEMBER
- return false;
+ return true;
}
-GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line, bool p_is_indexing) {
- if (p_base_type && !p_base_type->has_type) {
- return DataType();
- }
-
- DataType base_type;
- DataType member_type;
-
- if (!p_base_type) {
- base_type.has_type = true;
- base_type.is_constant = true;
- base_type.kind = DataType::CLASS;
- base_type.class_type = current_class;
- } else {
- base_type = DataType(*p_base_type);
- }
-
- bool is_const = false;
- if (_get_member_type(base_type, p_identifier, member_type, &is_const)) {
- if (!p_base_type && current_function && current_function->_static && !is_const) {
- _set_error("Can't access member variable (\"" + p_identifier.operator String() + "\") from a static function.", p_line);
+GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype() const {
+ switch (type) {
+ case CONSTANT:
+ return constant->get_datatype();
+ case VARIABLE:
+ return variable->get_datatype();
+ case PARAMETER:
+ return parameter->get_datatype();
+ case FOR_VARIABLE:
+ case PATTERN_BIND:
+ return bind->get_datatype();
+ case UNDEFINED:
return DataType();
- }
- return member_type;
}
+ return DataType();
+}
- if (p_is_indexing) {
- // Don't look for globals since this is an indexed identifier
- return DataType();
+String GDScriptParser::SuiteNode::Local::get_name() const {
+ String name;
+ switch (type) {
+ case SuiteNode::Local::PARAMETER:
+ name = "parameter";
+ break;
+ case SuiteNode::Local::CONSTANT:
+ name = "constant";
+ break;
+ case SuiteNode::Local::VARIABLE:
+ name = "variable";
+ break;
+ case SuiteNode::Local::FOR_VARIABLE:
+ name = "for loop iterator";
+ break;
+ case SuiteNode::Local::PATTERN_BIND:
+ name = "pattern_bind";
+ break;
+ case SuiteNode::Local::UNDEFINED:
+ name = "<undefined>";
+ break;
}
+ return name;
+}
- if (!p_base_type) {
- // Possibly this is a global, check before failing
-
- if (ClassDB::class_exists(p_identifier) || ClassDB::class_exists("_" + p_identifier.operator String())) {
- DataType result;
- result.has_type = true;
- result.is_constant = true;
- result.is_meta_type = true;
- if (Engine::get_singleton()->has_singleton(p_identifier) || Engine::get_singleton()->has_singleton("_" + p_identifier.operator String())) {
- result.is_meta_type = false;
+String GDScriptParser::DataType::to_string() const {
+ switch (kind) {
+ case VARIANT:
+ return "Variant";
+ case BUILTIN:
+ if (builtin_type == Variant::NIL) {
+ return "null";
}
- result.kind = DataType::NATIVE;
- result.native_type = p_identifier;
- return result;
- }
-
- ClassNode *outer_class = current_class;
- while (outer_class) {
- if (outer_class->name == p_identifier) {
- DataType result;
- result.has_type = true;
- result.is_constant = true;
- result.is_meta_type = true;
- result.kind = DataType::CLASS;
- result.class_type = outer_class;
- return result;
+ return Variant::get_type_name(builtin_type);
+ case NATIVE:
+ if (is_meta_type) {
+ return GDScriptNativeClass::get_class_static();
}
- if (outer_class->constant_expressions.has(p_identifier)) {
- return outer_class->constant_expressions[p_identifier].type;
+ return native_type.operator String();
+ case CLASS:
+ if (is_meta_type) {
+ return GDScript::get_class_static();
}
- for (int i = 0; i < outer_class->subclasses.size(); i++) {
- if (outer_class->subclasses[i] == current_class) {
- continue;
- }
- if (outer_class->subclasses[i]->name == p_identifier) {
- DataType result;
- result.has_type = true;
- result.is_constant = true;
- result.is_meta_type = true;
- result.kind = DataType::CLASS;
- result.class_type = outer_class->subclasses[i];
- return result;
- }
+ if (class_type->identifier != nullptr) {
+ return class_type->identifier->name.operator String();
}
- outer_class = outer_class->owner;
- }
-
- if (ScriptServer::is_global_class(p_identifier)) {
- Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
- if (scr.is_valid()) {
- DataType result;
- result.has_type = true;
- result.script_type = scr;
- result.is_constant = true;
- result.is_meta_type = true;
- Ref<GDScript> gds = scr;
- if (gds.is_valid()) {
- if (!gds->is_valid()) {
- _set_error("The class \"" + p_identifier + "\" couldn't be fully loaded (script error or cyclic dependency).");
- return DataType();
- }
- result.kind = DataType::GDSCRIPT;
- } else {
- result.kind = DataType::SCRIPT;
- }
- return result;
+ return class_type->fqcn;
+ case SCRIPT: {
+ if (is_meta_type) {
+ return script_type->get_class_name().operator String();
}
- _set_error("The class \"" + p_identifier + "\" was found in global scope, but its script couldn't be loaded.");
- return DataType();
- }
-
- if (GDScriptLanguage::get_singleton()->get_global_map().has(p_identifier)) {
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[p_identifier];
- Variant g = GDScriptLanguage::get_singleton()->get_global_array()[idx];
- return _type_from_variant(g);
- }
-
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
- Variant g = GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier];
- return _type_from_variant(g);
- }
-
- // Non-tool singletons aren't loaded, check project settings
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
-
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
+ String name = script_type->get_name();
+ if (!name.empty()) {
+ return name;
}
- String name = s.get_slice("/", 1);
- if (name == p_identifier) {
- String script = ProjectSettings::get_singleton()->get(s);
- if (script.begins_with("*")) {
- script = script.right(1);
- }
- if (!script.begins_with("res://")) {
- script = "res://" + script;
- }
- Ref<Script> singleton = ResourceLoader::load(script);
- if (singleton.is_valid()) {
- DataType result;
- result.has_type = true;
- result.is_constant = true;
- result.script_type = singleton;
-
- Ref<GDScript> gds = singleton;
- if (gds.is_valid()) {
- if (!gds->is_valid()) {
- _set_error("Couldn't fully load the singleton script \"" + p_identifier + "\" (possible cyclic reference or parse error).", p_line);
- return DataType();
- }
- result.kind = DataType::GDSCRIPT;
- } else {
- result.kind = DataType::SCRIPT;
- }
- }
+ name = script_path;
+ if (!name.empty()) {
+ return name;
}
+ return native_type.operator String();
}
-
- // This means looking in the current class, which type is always known
- _set_error("The identifier \"" + p_identifier.operator String() + "\" isn't declared in the current scope.", p_line);
- }
-
-#ifdef DEBUG_ENABLED
- {
- DataType tmp_type;
- List<DataType> arg_types;
- int argcount;
- bool _static;
- bool vararg;
- if (_get_function_signature(base_type, p_identifier, tmp_type, arg_types, argcount, _static, vararg)) {
- _add_warning(GDScriptWarning::FUNCTION_USED_AS_PROPERTY, p_line, p_identifier.operator String(), base_type.to_string());
- }
+ case ENUM:
+ return enum_type.operator String() + " (enum)";
+ case ENUM_VALUE:
+ return enum_type.operator String() + " (enum value)";
+ case UNRESOLVED:
+ return "<unresolved type>";
}
-#endif // DEBUG_ENABLED
- _mark_line_as_unsafe(p_line);
- return DataType();
+ ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range.");
}
-void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
- // Names of internal object properties that we check to avoid overriding them.
- // "__meta__" could also be in here, but since it doesn't really affect object metadata,
- // it is okay to override it on script.
- StringName script_name = CoreStringNames::get_singleton()->_script;
+/*---------- PRETTY PRINT FOR DEBUG ----------*/
- _mark_line_as_safe(p_class->line);
-
- // Constants
- for (Map<StringName, ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
- ClassNode::Constant &c = E->get();
- _mark_line_as_safe(c.expression->line);
- DataType cont = _resolve_type(c.type, c.expression->line);
- DataType expr = _resolve_type(c.expression->get_datatype(), c.expression->line);
-
- if (check_types && !_is_type_compatible(cont, expr)) {
- _set_error("The constant value type (" + expr.to_string() + ") isn't compatible with declared type (" + cont.to_string() + ").",
- c.expression->line);
- return;
- }
-
- expr.is_constant = true;
- c.type = expr;
- c.expression->set_datatype(expr);
+#ifdef DEBUG_ENABLED
- DataType tmp;
- const StringName &constant_name = E->key();
- if (constant_name == script_name || _get_member_type(p_class->base_type, constant_name, tmp)) {
- _set_error("The member \"" + String(constant_name) + "\" already exists in a parent class.", c.expression->line);
- return;
+void GDScriptParser::TreePrinter::increase_indent() {
+ indent_level++;
+ indent = "";
+ for (int i = 0; i < indent_level * 4; i++) {
+ if (i % 4 == 0) {
+ indent += "|";
+ } else {
+ indent += " ";
}
}
+}
- // Function declarations
- for (int i = 0; i < p_class->static_functions.size(); i++) {
- _check_function_types(p_class->static_functions[i]);
- if (error_set) {
- return;
+void GDScriptParser::TreePrinter::decrease_indent() {
+ indent_level--;
+ indent = "";
+ for (int i = 0; i < indent_level * 4; i++) {
+ if (i % 4 == 0) {
+ indent += "|";
+ } else {
+ indent += " ";
}
}
+}
- for (int i = 0; i < p_class->functions.size(); i++) {
- _check_function_types(p_class->functions[i]);
- if (error_set) {
- return;
- }
+void GDScriptParser::TreePrinter::push_line(const String &p_line) {
+ if (!p_line.empty()) {
+ push_text(p_line);
}
+ printed += "\n";
+ pending_indent = true;
+}
- // Class variables
- for (int i = 0; i < p_class->variables.size(); i++) {
- ClassNode::Member &v = p_class->variables.write[i];
-
- DataType tmp;
- if (v.identifier == script_name || _get_member_type(p_class->base_type, v.identifier, tmp)) {
- _set_error("The member \"" + String(v.identifier) + "\" already exists in a parent class.", v.line);
- return;
- }
-
- _mark_line_as_safe(v.line);
- v.data_type = _resolve_type(v.data_type, v.line);
- v.initial_assignment->arguments[0]->set_datatype(v.data_type);
-
- if (v.expression) {
- DataType expr_type = _reduce_node_type(v.expression);
-
- if (check_types && !_is_type_compatible(v.data_type, expr_type)) {
- // Try supertype test
- if (_is_type_compatible(expr_type, v.data_type)) {
- _mark_line_as_unsafe(v.line);
- } else {
- // Try with implicit conversion
- if (v.data_type.kind != DataType::BUILTIN || !_is_type_compatible(v.data_type, expr_type, true)) {
- _set_error("The assigned expression's type (" + expr_type.to_string() + ") doesn't match the variable's type (" +
- v.data_type.to_string() + ").",
- v.line);
- return;
- }
-
- // Replace assignment with implicit conversion
- BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
- convert->line = v.line;
- convert->function = GDScriptFunctions::TYPE_CONVERT;
-
- ConstantNode *tgt_type = alloc_node<ConstantNode>();
- tgt_type->line = v.line;
- tgt_type->value = (int64_t)v.data_type.builtin_type;
-
- OperatorNode *convert_call = alloc_node<OperatorNode>();
- convert_call->line = v.line;
- convert_call->op = OperatorNode::OP_CALL;
- convert_call->arguments.push_back(convert);
- convert_call->arguments.push_back(v.expression);
- convert_call->arguments.push_back(tgt_type);
-
- v.expression = convert_call;
- v.initial_assignment->arguments.write[1] = convert_call;
- }
- }
+void GDScriptParser::TreePrinter::push_text(const String &p_text) {
+ if (pending_indent) {
+ printed += indent;
+ pending_indent = false;
+ }
+ printed += p_text;
+}
- if (v.data_type.infer_type) {
- if (!expr_type.has_type) {
- _set_error("The assigned value doesn't have a set type; the variable type can't be inferred.", v.line);
- return;
- }
- if (expr_type.kind == DataType::BUILTIN && expr_type.builtin_type == Variant::NIL) {
- _set_error("The variable type cannot be inferred because its value is \"null\".", v.line);
- return;
- }
- v.data_type = expr_type;
- v.data_type.is_constant = false;
- }
+void GDScriptParser::TreePrinter::print_annotation(AnnotationNode *p_annotation) {
+ push_text(p_annotation->name);
+ push_text(" (");
+ for (int i = 0; i < p_annotation->arguments.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
}
+ print_expression(p_annotation->arguments[i]);
+ }
+ push_line(")");
+}
- // Check export hint
- if (v.data_type.has_type && v._export.type != Variant::NIL) {
- DataType export_type = _type_from_property(v._export);
- if (!_is_type_compatible(v.data_type, export_type, true)) {
- _set_error("The export hint's type (" + export_type.to_string() + ") doesn't match the variable's type (" +
- v.data_type.to_string() + ").",
- v.line);
- return;
- }
+void GDScriptParser::TreePrinter::print_array(ArrayNode *p_array) {
+ push_text("[ ");
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
}
+ print_expression(p_array->elements[i]);
+ }
+ push_text(" ]");
+}
- // Setter and getter
- if (v.setter == StringName() && v.getter == StringName()) {
- continue;
- }
+void GDScriptParser::TreePrinter::print_assert(AssertNode *p_assert) {
+ push_text("Assert ( ");
+ print_expression(p_assert->condition);
+ push_line(" )");
+}
- bool found_getter = false;
- bool found_setter = false;
- for (int j = 0; j < p_class->functions.size(); j++) {
- if (v.setter == p_class->functions[j]->name) {
- found_setter = true;
- FunctionNode *setter = p_class->functions[j];
-
- if (setter->get_required_argument_count() != 1 &&
- !(setter->get_required_argument_count() == 0 && setter->default_values.size() > 0)) {
- _set_error("The setter function needs to receive exactly 1 argument. See \"" + setter->name +
- "()\" definition at line " + itos(setter->line) + ".",
- v.line);
- return;
- }
- if (!_is_type_compatible(v.data_type, setter->argument_types[0])) {
- _set_error("The setter argument's type (" + setter->argument_types[0].to_string() +
- ") doesn't match the variable's type (" + v.data_type.to_string() + "). See \"" +
- setter->name + "()\" definition at line " + itos(setter->line) + ".",
- v.line);
- return;
- }
- continue;
- }
- if (v.getter == p_class->functions[j]->name) {
- found_getter = true;
- FunctionNode *getter = p_class->functions[j];
-
- if (getter->get_required_argument_count() != 0) {
- _set_error("The getter function can't receive arguments. See \"" + getter->name +
- "()\" definition at line " + itos(getter->line) + ".",
- v.line);
- return;
- }
- if (!_is_type_compatible(v.data_type, getter->get_datatype())) {
- _set_error("The getter return type (" + getter->get_datatype().to_string() +
- ") doesn't match the variable's type (" + v.data_type.to_string() +
- "). See \"" + getter->name + "()\" definition at line " + itos(getter->line) + ".",
- v.line);
- return;
- }
- }
- if (found_getter && found_setter) {
- break;
- }
- }
+void GDScriptParser::TreePrinter::print_assignment(AssignmentNode *p_assignment) {
+ switch (p_assignment->assignee->type) {
+ case Node::IDENTIFIER:
+ print_identifier(static_cast<IdentifierNode *>(p_assignment->assignee));
+ break;
+ case Node::SUBSCRIPT:
+ print_subscript(static_cast<SubscriptNode *>(p_assignment->assignee));
+ break;
+ default:
+ break; // Unreachable.
+ }
- if ((found_getter || v.getter == StringName()) && (found_setter || v.setter == StringName())) {
- continue;
- }
+ push_text(" ");
+ switch (p_assignment->operation) {
+ case AssignmentNode::OP_ADDITION:
+ push_text("+");
+ break;
+ case AssignmentNode::OP_SUBTRACTION:
+ push_text("-");
+ break;
+ case AssignmentNode::OP_MULTIPLICATION:
+ push_text("*");
+ break;
+ case AssignmentNode::OP_DIVISION:
+ push_text("/");
+ break;
+ case AssignmentNode::OP_MODULO:
+ push_text("%");
+ break;
+ case AssignmentNode::OP_BIT_SHIFT_LEFT:
+ push_text("<<");
+ break;
+ case AssignmentNode::OP_BIT_SHIFT_RIGHT:
+ push_text(">>");
+ break;
+ case AssignmentNode::OP_BIT_AND:
+ push_text("&");
+ break;
+ case AssignmentNode::OP_BIT_OR:
+ push_text("|");
+ break;
+ case AssignmentNode::OP_BIT_XOR:
+ push_text("^");
+ break;
+ case AssignmentNode::OP_NONE:
+ break;
+ }
+ push_text("= ");
+ print_expression(p_assignment->assigned_value);
+ push_line();
+}
- // Check for static functions
- for (int j = 0; j < p_class->static_functions.size(); j++) {
- if (v.setter == p_class->static_functions[j]->name) {
- FunctionNode *setter = p_class->static_functions[j];
- _set_error("The setter can't be a static function. See \"" + setter->name + "()\" definition at line " + itos(setter->line) + ".", v.line);
- return;
- }
- if (v.getter == p_class->static_functions[j]->name) {
- FunctionNode *getter = p_class->static_functions[j];
- _set_error("The getter can't be a static function. See \"" + getter->name + "()\" definition at line " + itos(getter->line) + ".", v.line);
- return;
- }
- }
+void GDScriptParser::TreePrinter::print_await(AwaitNode *p_await) {
+ push_text("Await ");
+ print_expression(p_await->to_await);
+}
- if (!found_setter && v.setter != StringName()) {
- _set_error("The setter function isn't defined.", v.line);
- return;
- }
+void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) {
+ // Surround in parenthesis for disambiguation.
+ push_text("(");
+ print_expression(p_binary_op->left_operand);
+ switch (p_binary_op->operation) {
+ case BinaryOpNode::OP_ADDITION:
+ push_text(" + ");
+ break;
+ case BinaryOpNode::OP_SUBTRACTION:
+ push_text(" - ");
+ break;
+ case BinaryOpNode::OP_MULTIPLICATION:
+ push_text(" * ");
+ break;
+ case BinaryOpNode::OP_DIVISION:
+ push_text(" / ");
+ break;
+ case BinaryOpNode::OP_MODULO:
+ push_text(" % ");
+ break;
+ case BinaryOpNode::OP_BIT_LEFT_SHIFT:
+ push_text(" << ");
+ break;
+ case BinaryOpNode::OP_BIT_RIGHT_SHIFT:
+ push_text(" >> ");
+ break;
+ case BinaryOpNode::OP_BIT_AND:
+ push_text(" & ");
+ break;
+ case BinaryOpNode::OP_BIT_OR:
+ push_text(" | ");
+ break;
+ case BinaryOpNode::OP_BIT_XOR:
+ push_text(" ^ ");
+ break;
+ case BinaryOpNode::OP_LOGIC_AND:
+ push_text(" AND ");
+ break;
+ case BinaryOpNode::OP_LOGIC_OR:
+ push_text(" OR ");
+ break;
+ case BinaryOpNode::OP_TYPE_TEST:
+ push_text(" IS ");
+ break;
+ case BinaryOpNode::OP_CONTENT_TEST:
+ push_text(" IN ");
+ break;
+ case BinaryOpNode::OP_COMP_EQUAL:
+ push_text(" == ");
+ break;
+ case BinaryOpNode::OP_COMP_NOT_EQUAL:
+ push_text(" != ");
+ break;
+ case BinaryOpNode::OP_COMP_LESS:
+ push_text(" < ");
+ break;
+ case BinaryOpNode::OP_COMP_LESS_EQUAL:
+ push_text(" <= ");
+ break;
+ case BinaryOpNode::OP_COMP_GREATER:
+ push_text(" > ");
+ break;
+ case BinaryOpNode::OP_COMP_GREATER_EQUAL:
+ push_text(" >= ");
+ break;
+ }
+ print_expression(p_binary_op->right_operand);
+ // Surround in parenthesis for disambiguation.
+ push_text(")");
+}
- if (!found_getter && v.getter != StringName()) {
- _set_error("The getter function isn't defined.", v.line);
- return;
+void GDScriptParser::TreePrinter::print_call(CallNode *p_call) {
+ if (p_call->is_super) {
+ push_text("super");
+ if (p_call->callee != nullptr) {
+ push_text(".");
+ print_expression(p_call->callee);
}
+ } else {
+ print_expression(p_call->callee);
}
-
- // Inner classes
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- current_class = p_class->subclasses[i];
- _check_class_level_types(current_class);
- if (error_set) {
- return;
+ push_text("( ");
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
}
- current_class = p_class;
+ print_expression(p_call->arguments[i]);
}
+ push_text(" )");
}
-void GDScriptParser::_check_function_types(FunctionNode *p_function) {
- p_function->return_type = _resolve_type(p_function->return_type, p_function->line);
-
- // Arguments
- int defaults_ofs = p_function->arguments.size() - p_function->default_values.size();
- for (int i = 0; i < p_function->arguments.size(); i++) {
- if (i < defaults_ofs) {
- p_function->argument_types.write[i] = _resolve_type(p_function->argument_types[i], p_function->line);
- } else {
- if (p_function->default_values[i - defaults_ofs]->type != Node::TYPE_OPERATOR) {
- _set_error("Parser bug: invalid argument default value.", p_function->line, p_function->column);
- return;
- }
-
- OperatorNode *op = static_cast<OperatorNode *>(p_function->default_values[i - defaults_ofs]);
-
- if (op->op != OperatorNode::OP_ASSIGN || op->arguments.size() != 2) {
- _set_error("Parser bug: invalid argument default value operation.", p_function->line);
- return;
- }
+void GDScriptParser::TreePrinter::print_cast(CastNode *p_cast) {
+ print_expression(p_cast->operand);
+ push_text(" AS ");
+ print_type(p_cast->cast_type);
+}
- DataType def_type = _reduce_node_type(op->arguments[1]);
+void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {
+ push_text("Class ");
+ if (p_class->identifier == nullptr) {
+ push_text("<unnamed>");
+ } else {
+ print_identifier(p_class->identifier);
+ }
- if (p_function->argument_types[i].infer_type) {
- def_type.is_constant = false;
- p_function->argument_types.write[i] = def_type;
+ if (p_class->extends_used) {
+ bool first = true;
+ push_text(" Extends ");
+ if (!p_class->extends_path.empty()) {
+ push_text(vformat(R"("%s")", p_class->extends_path));
+ first = false;
+ }
+ for (int i = 0; i < p_class->extends.size(); i++) {
+ if (!first) {
+ push_text(".");
} else {
- p_function->argument_types.write[i] = _resolve_type(p_function->argument_types[i], p_function->line);
-
- if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) {
- String arg_name = p_function->arguments[i];
- _set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" +
- arg_name + "' (" + p_function->argument_types[i].to_string() + ").",
- p_function->line);
- }
+ first = false;
}
+ push_text(p_class->extends[i]);
}
-#ifdef DEBUG_ENABLED
- if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) {
- _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String());
- }
- for (int j = 0; j < current_class->variables.size(); j++) {
- if (current_class->variables[j].identifier == p_function->arguments[i]) {
- _add_warning(GDScriptWarning::SHADOWED_VARIABLE, p_function->line, p_function->arguments[i], itos(current_class->variables[j].line));
- }
- }
-#endif // DEBUG_ENABLED
}
- if (!(p_function->name == "_init")) {
- // Signature for the initializer may vary
-#ifdef DEBUG_ENABLED
- DataType return_type;
- List<DataType> arg_types;
- int default_arg_count = 0;
- bool _static = false;
- bool vararg = false;
-
- DataType base_type = current_class->base_type;
- if (_get_function_signature(base_type, p_function->name, return_type, arg_types, default_arg_count, _static, vararg)) {
- bool valid = _static == p_function->_static;
- valid = valid && return_type == p_function->return_type;
- int argsize_diff = p_function->arguments.size() - arg_types.size();
- valid = valid && argsize_diff >= 0;
- valid = valid && p_function->default_values.size() >= default_arg_count + argsize_diff;
- int i = 0;
- for (List<DataType>::Element *E = arg_types.front(); valid && E; E = E->next()) {
- valid = valid && E->get() == p_function->argument_types[i++];
- }
+ push_line(" :");
- if (!valid) {
- String parent_signature = return_type.has_type ? return_type.to_string() : "Variant";
- if (parent_signature == "null") {
- parent_signature = "void";
- }
- parent_signature += " " + p_function->name + "(";
- if (arg_types.size()) {
- int j = 0;
- for (List<DataType>::Element *E = arg_types.front(); E; E = E->next()) {
- if (E != arg_types.front()) {
- parent_signature += ", ";
- }
- String arg = E->get().to_string();
- if (arg == "null" || arg == "var") {
- arg = "Variant";
- }
- parent_signature += arg;
- if (j == arg_types.size() - default_arg_count) {
- parent_signature += "=default";
- }
+ increase_indent();
- j++;
- }
- }
- parent_signature += ")";
- _set_error("The function signature doesn't match the parent. Parent signature is: \"" + parent_signature + "\".", p_function->line);
- return;
- }
- }
-#endif // DEBUG_ENABLED
- } else {
- if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
- _set_error("The constructor can't return a value.", p_function->line);
- return;
- }
- }
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const ClassNode::Member &m = p_class->members[i];
- if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) {
- if (!p_function->body->has_return) {
- _set_error("A non-void function must return a value in all possible paths.", p_function->line);
- return;
+ switch (m.type) {
+ case ClassNode::Member::CLASS:
+ print_class(m.m_class);
+ break;
+ case ClassNode::Member::VARIABLE:
+ print_variable(m.variable);
+ break;
+ case ClassNode::Member::CONSTANT:
+ print_constant(m.constant);
+ break;
+ case ClassNode::Member::SIGNAL:
+ print_signal(m.signal);
+ break;
+ case ClassNode::Member::FUNCTION:
+ print_function(m.function);
+ break;
+ case ClassNode::Member::ENUM:
+ print_enum(m.m_enum);
+ break;
+ case ClassNode::Member::ENUM_VALUE:
+ break; // Nothing. Will be printed by enum.
+ case ClassNode::Member::UNDEFINED:
+ push_line("<unknown member>");
+ break;
}
}
- if (p_function->has_yield) {
- // yield() will make the function return a GDScriptFunctionState, so the type is ambiguous
- p_function->return_type.has_type = false;
- p_function->return_type.may_yield = true;
- }
+ decrease_indent();
}
-void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) {
- // Function blocks
- for (int i = 0; i < p_class->static_functions.size(); i++) {
- current_function = p_class->static_functions[i];
- current_block = current_function->body;
- _mark_line_as_safe(current_function->line);
- _check_block_types(current_block);
- current_block = nullptr;
- current_function = nullptr;
- if (error_set) {
- return;
- }
- }
+void GDScriptParser::TreePrinter::print_constant(ConstantNode *p_constant) {
+ push_text("Constant ");
+ print_identifier(p_constant->identifier);
- for (int i = 0; i < p_class->functions.size(); i++) {
- current_function = p_class->functions[i];
- current_block = current_function->body;
- _mark_line_as_safe(current_function->line);
- _check_block_types(current_block);
- current_block = nullptr;
- current_function = nullptr;
- if (error_set) {
- return;
- }
- }
+ increase_indent();
-#ifdef DEBUG_ENABLED
- // Warnings
- for (int i = 0; i < p_class->variables.size(); i++) {
- if (p_class->variables[i].usages == 0) {
- _add_warning(GDScriptWarning::UNUSED_CLASS_VARIABLE, p_class->variables[i].line, p_class->variables[i].identifier);
- }
- }
- for (int i = 0; i < p_class->_signals.size(); i++) {
- if (p_class->_signals[i].emissions == 0) {
- _add_warning(GDScriptWarning::UNUSED_SIGNAL, p_class->_signals[i].line, p_class->_signals[i].name);
- }
+ push_line();
+ push_text("= ");
+ if (p_constant->initializer == nullptr) {
+ push_text("<missing value>");
+ } else {
+ print_expression(p_constant->initializer);
}
-#endif // DEBUG_ENABLED
+ decrease_indent();
+ push_line();
+}
- // Inner classes
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- current_class = p_class->subclasses[i];
- _check_class_blocks_types(current_class);
- if (error_set) {
- return;
+void GDScriptParser::TreePrinter::print_dictionary(DictionaryNode *p_dictionary) {
+ push_line("{");
+ increase_indent();
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ print_expression(p_dictionary->elements[i].key);
+ if (p_dictionary->style == DictionaryNode::PYTHON_DICT) {
+ push_text(" : ");
+ } else {
+ push_text(" = ");
}
- current_class = p_class;
+ print_expression(p_dictionary->elements[i].value);
+ push_line(" ,");
}
+ decrease_indent();
+ push_text("}");
}
-#ifdef DEBUG_ENABLED
-static String _find_function_name(const GDScriptParser::OperatorNode *p_call) {
- switch (p_call->arguments[0]->type) {
- case GDScriptParser::Node::TYPE_TYPE: {
- return Variant::get_type_name(static_cast<GDScriptParser::TypeNode *>(p_call->arguments[0])->vtype);
- } break;
- case GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION: {
- return GDScriptFunctions::get_func_name(static_cast<GDScriptParser::BuiltInFunctionNode *>(p_call->arguments[0])->function);
- } break;
- default: {
- int id_index = p_call->op == GDScriptParser::OperatorNode::OP_PARENT_CALL ? 0 : 1;
- if (p_call->arguments.size() > id_index && p_call->arguments[id_index]->type == GDScriptParser::Node::TYPE_IDENTIFIER) {
- return static_cast<GDScriptParser::IdentifierNode *>(p_call->arguments[id_index])->name;
- }
- } break;
+void GDScriptParser::TreePrinter::print_expression(ExpressionNode *p_expression) {
+ switch (p_expression->type) {
+ case Node::ARRAY:
+ print_array(static_cast<ArrayNode *>(p_expression));
+ break;
+ case Node::ASSIGNMENT:
+ print_assignment(static_cast<AssignmentNode *>(p_expression));
+ break;
+ case Node::AWAIT:
+ print_await(static_cast<AwaitNode *>(p_expression));
+ break;
+ case Node::BINARY_OPERATOR:
+ print_binary_op(static_cast<BinaryOpNode *>(p_expression));
+ break;
+ case Node::CALL:
+ print_call(static_cast<CallNode *>(p_expression));
+ break;
+ case Node::CAST:
+ print_cast(static_cast<CastNode *>(p_expression));
+ break;
+ case Node::DICTIONARY:
+ print_dictionary(static_cast<DictionaryNode *>(p_expression));
+ break;
+ case Node::GET_NODE:
+ print_get_node(static_cast<GetNodeNode *>(p_expression));
+ break;
+ case Node::IDENTIFIER:
+ print_identifier(static_cast<IdentifierNode *>(p_expression));
+ break;
+ case Node::LITERAL:
+ print_literal(static_cast<LiteralNode *>(p_expression));
+ break;
+ case Node::PRELOAD:
+ print_preload(static_cast<PreloadNode *>(p_expression));
+ break;
+ case Node::SELF:
+ print_self(static_cast<SelfNode *>(p_expression));
+ break;
+ case Node::SUBSCRIPT:
+ print_subscript(static_cast<SubscriptNode *>(p_expression));
+ break;
+ case Node::TERNARY_OPERATOR:
+ print_ternary_op(static_cast<TernaryOpNode *>(p_expression));
+ break;
+ case Node::UNARY_OPERATOR:
+ print_unary_op(static_cast<UnaryOpNode *>(p_expression));
+ break;
+ default:
+ push_text(vformat("<unknown expression %d>", p_expression->type));
+ break;
}
- return String();
}
-#endif // DEBUG_ENABLED
-void GDScriptParser::_check_block_types(BlockNode *p_block) {
- Node *last_var_assign = nullptr;
-
- // Check each statement
- for (List<Node *>::Element *E = p_block->statements.front(); E; E = E->next()) {
- Node *statement = E->get();
- switch (statement->type) {
- case Node::TYPE_NEWLINE:
- case Node::TYPE_BREAKPOINT: {
- // Nothing to do
- } break;
- case Node::TYPE_ASSERT: {
- AssertNode *an = static_cast<AssertNode *>(statement);
- _mark_line_as_safe(an->line);
- _reduce_node_type(an->condition);
- _reduce_node_type(an->message);
- } break;
- case Node::TYPE_LOCAL_VAR: {
- LocalVarNode *lv = static_cast<LocalVarNode *>(statement);
- lv->datatype = _resolve_type(lv->datatype, lv->line);
- _mark_line_as_safe(lv->line);
-
- last_var_assign = lv->assign;
- if (lv->assign) {
- lv->assign_op->arguments[0]->set_datatype(lv->datatype);
- DataType assign_type = _reduce_node_type(lv->assign);
-#ifdef DEBUG_ENABLED
- if (assign_type.has_type && assign_type.kind == DataType::BUILTIN && assign_type.builtin_type == Variant::NIL) {
- if (lv->assign->type == Node::TYPE_OPERATOR) {
- OperatorNode *call = static_cast<OperatorNode *>(lv->assign);
- if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) {
- _add_warning(GDScriptWarning::VOID_ASSIGNMENT, lv->line, _find_function_name(call));
- }
- }
- }
- if (lv->datatype.has_type && assign_type.may_yield && lv->assign->type == Node::TYPE_OPERATOR) {
- _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, lv->line, _find_function_name(static_cast<OperatorNode *>(lv->assign)));
- }
- for (int i = 0; i < current_class->variables.size(); i++) {
- if (current_class->variables[i].identifier == lv->name) {
- _add_warning(GDScriptWarning::SHADOWED_VARIABLE, lv->line, lv->name, itos(current_class->variables[i].line));
- }
- }
-#endif // DEBUG_ENABLED
-
- if (!_is_type_compatible(lv->datatype, assign_type)) {
- // Try supertype test
- if (_is_type_compatible(assign_type, lv->datatype)) {
- _mark_line_as_unsafe(lv->line);
- } else {
- // Try implicit conversion
- if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) {
- _set_error("The assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" +
- lv->datatype.to_string() + ").",
- lv->line);
- return;
- }
- // Replace assignment with implicit conversion
- BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
- convert->line = lv->line;
- convert->function = GDScriptFunctions::TYPE_CONVERT;
-
- ConstantNode *tgt_type = alloc_node<ConstantNode>();
- tgt_type->line = lv->line;
- tgt_type->value = (int64_t)lv->datatype.builtin_type;
- tgt_type->datatype = _type_from_variant(tgt_type->value);
-
- OperatorNode *convert_call = alloc_node<OperatorNode>();
- convert_call->line = lv->line;
- convert_call->op = OperatorNode::OP_CALL;
- convert_call->arguments.push_back(convert);
- convert_call->arguments.push_back(lv->assign);
- convert_call->arguments.push_back(tgt_type);
-
- lv->assign = convert_call;
- lv->assign_op->arguments.write[1] = convert_call;
-#ifdef DEBUG_ENABLED
- if (lv->datatype.builtin_type == Variant::INT && assign_type.builtin_type == Variant::FLOAT) {
- _add_warning(GDScriptWarning::NARROWING_CONVERSION, lv->line);
- }
-#endif // DEBUG_ENABLED
- }
- }
- if (lv->datatype.infer_type) {
- if (!assign_type.has_type) {
- _set_error("The assigned value doesn't have a set type; the variable type can't be inferred.", lv->line);
- return;
- }
- if (assign_type.kind == DataType::BUILTIN && assign_type.builtin_type == Variant::NIL) {
- _set_error("The variable type cannot be inferred because its value is \"null\".", lv->line);
- return;
- }
- lv->datatype = assign_type;
- lv->datatype.is_constant = false;
- }
- if (lv->datatype.has_type && !assign_type.has_type) {
- _mark_line_as_unsafe(lv->line);
- }
- }
- } break;
- case Node::TYPE_OPERATOR: {
- OperatorNode *op = static_cast<OperatorNode *>(statement);
-
- switch (op->op) {
- case OperatorNode::OP_ASSIGN:
- case OperatorNode::OP_ASSIGN_ADD:
- case OperatorNode::OP_ASSIGN_SUB:
- case OperatorNode::OP_ASSIGN_MUL:
- case OperatorNode::OP_ASSIGN_DIV:
- case OperatorNode::OP_ASSIGN_MOD:
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
- case OperatorNode::OP_ASSIGN_BIT_AND:
- case OperatorNode::OP_ASSIGN_BIT_OR:
- case OperatorNode::OP_ASSIGN_BIT_XOR: {
- if (op->arguments.size() < 2) {
- _set_error("Parser bug: operation without enough arguments.", op->line, op->column);
- return;
- }
-
- if (op->arguments[1] == last_var_assign) {
- // Assignment was already checked
- break;
- }
-
- _mark_line_as_safe(op->line);
-
- DataType lh_type = _reduce_node_type(op->arguments[0]);
-
- if (error_set) {
- return;
- }
-
- if (check_types) {
- if (!lh_type.has_type) {
- if (op->arguments[0]->type == Node::TYPE_OPERATOR) {
- _mark_line_as_unsafe(op->line);
- }
- }
- if (lh_type.is_constant) {
- _set_error("Can't assign a new value to a constant.", op->line);
- return;
- }
- }
-
- DataType rh_type;
- if (op->op != OperatorNode::OP_ASSIGN) {
- // Validate operation
- DataType arg_type = _reduce_node_type(op->arguments[1]);
- if (!arg_type.has_type) {
- _mark_line_as_unsafe(op->line);
- break;
- }
-
- Variant::Operator oper = _get_variant_operation(op->op);
- bool valid = false;
- rh_type = _get_operation_type(oper, lh_type, arg_type, valid);
+void GDScriptParser::TreePrinter::print_enum(EnumNode *p_enum) {
+ push_text("Enum ");
+ if (p_enum->identifier != nullptr) {
+ print_identifier(p_enum->identifier);
+ } else {
+ push_text("<unnamed>");
+ }
- if (check_types && !valid) {
- _set_error("Invalid operand types (\"" + lh_type.to_string() + "\" and \"" + arg_type.to_string() +
- "\") to assignment operator \"" + Variant::get_operator_name(oper) + "\".",
- op->line);
- return;
- }
- } else {
- rh_type = _reduce_node_type(op->arguments[1]);
- }
-#ifdef DEBUG_ENABLED
- if (rh_type.has_type && rh_type.kind == DataType::BUILTIN && rh_type.builtin_type == Variant::NIL) {
- if (op->arguments[1]->type == Node::TYPE_OPERATOR) {
- OperatorNode *call = static_cast<OperatorNode *>(op->arguments[1]);
- if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) {
- _add_warning(GDScriptWarning::VOID_ASSIGNMENT, op->line, _find_function_name(call));
- }
- }
- }
- if (lh_type.has_type && rh_type.may_yield && op->arguments[1]->type == Node::TYPE_OPERATOR) {
- _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, op->line, _find_function_name(static_cast<OperatorNode *>(op->arguments[1])));
- }
+ push_line(" {");
+ increase_indent();
+ for (int i = 0; i < p_enum->values.size(); i++) {
+ const EnumNode::Value &item = p_enum->values[i];
+ print_identifier(item.identifier);
+ push_text(" = ");
+ push_text(itos(item.value));
+ push_line(" ,");
+ }
+ decrease_indent();
+ push_line("}");
+}
-#endif // DEBUG_ENABLED
- bool type_match = lh_type.has_type && rh_type.has_type;
- if (check_types && !_is_type_compatible(lh_type, rh_type)) {
- type_match = false;
+void GDScriptParser::TreePrinter::print_for(ForNode *p_for) {
+ push_text("For ");
+ print_identifier(p_for->variable);
+ push_text(" IN ");
+ print_expression(p_for->list);
+ push_line(" :");
- // Try supertype test
- if (_is_type_compatible(rh_type, lh_type)) {
- _mark_line_as_unsafe(op->line);
- } else {
- // Try implicit conversion
- if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) {
- _set_error("The assigned value's type (" + rh_type.to_string() + ") doesn't match the variable's type (" +
- lh_type.to_string() + ").",
- op->line);
- return;
- }
- if (op->op == OperatorNode::OP_ASSIGN) {
- // Replace assignment with implicit conversion
- BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
- convert->line = op->line;
- convert->function = GDScriptFunctions::TYPE_CONVERT;
-
- ConstantNode *tgt_type = alloc_node<ConstantNode>();
- tgt_type->line = op->line;
- tgt_type->value = (int)lh_type.builtin_type;
- tgt_type->datatype = _type_from_variant(tgt_type->value);
-
- OperatorNode *convert_call = alloc_node<OperatorNode>();
- convert_call->line = op->line;
- convert_call->op = OperatorNode::OP_CALL;
- convert_call->arguments.push_back(convert);
- convert_call->arguments.push_back(op->arguments[1]);
- convert_call->arguments.push_back(tgt_type);
-
- op->arguments.write[1] = convert_call;
-
- type_match = true; // Since we are converting, the type is matching
- }
-#ifdef DEBUG_ENABLED
- if (lh_type.builtin_type == Variant::INT && rh_type.builtin_type == Variant::FLOAT) {
- _add_warning(GDScriptWarning::NARROWING_CONVERSION, op->line);
- }
-#endif // DEBUG_ENABLED
- }
- }
-#ifdef DEBUG_ENABLED
- if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) {
- _mark_line_as_unsafe(op->line);
- }
-#endif // DEBUG_ENABLED
- op->datatype.has_type = type_match;
- } break;
- case OperatorNode::OP_CALL:
- case OperatorNode::OP_PARENT_CALL: {
- _mark_line_as_safe(op->line);
- DataType func_type = _reduce_function_call_type(op);
-#ifdef DEBUG_ENABLED
- if (func_type.has_type && (func_type.kind != DataType::BUILTIN || func_type.builtin_type != Variant::NIL)) {
- // Figure out function name for warning
- String func_name = _find_function_name(op);
- if (func_name.empty()) {
- func_name = "<undetected name>";
- }
- _add_warning(GDScriptWarning::RETURN_VALUE_DISCARDED, op->line, func_name);
- }
-#endif // DEBUG_ENABLED
- if (error_set) {
- return;
- }
- } break;
- case OperatorNode::OP_YIELD: {
- _mark_line_as_safe(op->line);
- _reduce_node_type(op);
- } break;
- default: {
- _mark_line_as_safe(op->line);
- _reduce_node_type(op); // Test for safety anyway
-#ifdef DEBUG_ENABLED
- if (op->op == OperatorNode::OP_TERNARY_IF) {
- _add_warning(GDScriptWarning::STANDALONE_TERNARY, statement->line);
- } else {
- _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
- }
-#endif // DEBUG_ENABLED
- }
- }
- } break;
- case Node::TYPE_CONTROL_FLOW: {
- ControlFlowNode *cf = static_cast<ControlFlowNode *>(statement);
- _mark_line_as_safe(cf->line);
-
- switch (cf->cf_type) {
- case ControlFlowNode::CF_RETURN: {
- DataType function_type = current_function->get_datatype();
-
- DataType ret_type;
- if (cf->arguments.size() > 0) {
- ret_type = _reduce_node_type(cf->arguments[0]);
- if (error_set) {
- return;
- }
- }
+ increase_indent();
- if (!function_type.has_type) {
- break;
- }
+ print_suite(p_for->loop);
- if (function_type.kind == DataType::BUILTIN && function_type.builtin_type == Variant::NIL) {
- // Return void, should not have arguments
- if (cf->arguments.size() > 0) {
- _set_error("A void function cannot return a value.", cf->line, cf->column);
- return;
- }
- } else {
- // Return something, cannot be empty
- if (cf->arguments.size() == 0) {
- _set_error("A non-void function must return a value.", cf->line, cf->column);
- return;
- }
+ decrease_indent();
+}
- if (!_is_type_compatible(function_type, ret_type)) {
- _set_error("The returned value type (" + ret_type.to_string() + ") doesn't match the function return type (" +
- function_type.to_string() + ").",
- cf->line, cf->column);
- return;
- }
- }
- } break;
- case ControlFlowNode::CF_MATCH: {
- MatchNode *match_node = cf->match;
- _transform_match_statment(match_node);
- } break;
- default: {
- if (cf->body_else) {
- _mark_line_as_safe(cf->body_else->line);
- }
- for (int i = 0; i < cf->arguments.size(); i++) {
- _reduce_node_type(cf->arguments[i]);
- }
- } break;
- }
- } break;
- case Node::TYPE_CONSTANT: {
- ConstantNode *cn = static_cast<ConstantNode *>(statement);
- // Strings are fine since they can be multiline comments
- if (cn->value.get_type() == Variant::STRING) {
- break;
- }
- [[fallthrough]];
- }
- default: {
- _mark_line_as_safe(statement->line);
- _reduce_node_type(statement); // Test for safety anyway
-#ifdef DEBUG_ENABLED
- _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line);
-#endif // DEBUG_ENABLED
- }
- }
+void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function) {
+ for (const List<AnnotationNode *>::Element *E = p_function->annotations.front(); E != nullptr; E = E->next()) {
+ print_annotation(E->get());
}
-
- // Parse sub blocks
- for (int i = 0; i < p_block->sub_blocks.size(); i++) {
- current_block = p_block->sub_blocks[i];
- _check_block_types(current_block);
- current_block = p_block;
- if (error_set) {
- return;
+ push_text("Function ");
+ print_identifier(p_function->identifier);
+ push_text("( ");
+ for (int i = 0; i < p_function->parameters.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
}
+ print_parameter(p_function->parameters[i]);
}
+ push_line(" ) :");
+ increase_indent();
+ print_suite(p_function->body);
+ decrease_indent();
+}
-#ifdef DEBUG_ENABLED
- // Warnings check
- for (Map<StringName, LocalVarNode *>::Element *E = p_block->variables.front(); E; E = E->next()) {
- LocalVarNode *lv = E->get();
- if (!lv->name.operator String().begins_with("_")) {
- if (lv->usages == 0) {
- _add_warning(GDScriptWarning::UNUSED_VARIABLE, lv->line, lv->name);
- } else if (lv->assignments == 0) {
- _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE, lv->line, lv->name);
+void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) {
+ push_text("$");
+ if (p_get_node->string != nullptr) {
+ print_literal(p_get_node->string);
+ } else {
+ for (int i = 0; i < p_get_node->chain.size(); i++) {
+ if (i > 0) {
+ push_text("/");
}
+ print_identifier(p_get_node->chain[i]);
}
}
-#endif // DEBUG_ENABLED
}
-void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column) {
- if (error_set) {
- return; //allow no further errors
- }
-
- error = p_error;
- error_line = p_line < 0 ? tokenizer->get_token_line() : p_line;
- error_column = p_column < 0 ? tokenizer->get_token_column() : p_column;
- error_set = true;
+void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) {
+ push_text(p_identifier->name);
}
-#ifdef DEBUG_ENABLED
-void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
- Vector<String> symbols;
- if (!p_symbol1.empty()) {
- symbols.push_back(p_symbol1);
- }
- if (!p_symbol2.empty()) {
- symbols.push_back(p_symbol2);
- }
- if (!p_symbol3.empty()) {
- symbols.push_back(p_symbol3);
- }
- if (!p_symbol4.empty()) {
- symbols.push_back(p_symbol4);
+void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
+ if (p_is_elif) {
+ push_text("Elif ");
+ } else {
+ push_text("If ");
}
- _add_warning(p_code, p_line, symbols);
-}
+ print_expression(p_if->condition);
+ push_line(" :");
-void GDScriptParser::_add_warning(int p_code, int p_line, const Vector<String> &p_symbols) {
- if (GLOBAL_GET("debug/gdscript/warnings/exclude_addons").booleanize() && base_path.begins_with("res://addons/")) {
- return;
- }
- if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
- return;
- }
- String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower();
- if (tokenizer->get_warning_global_skips().has(warn_name)) {
- return;
- }
- if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) {
- return;
- }
+ increase_indent();
+ print_suite(p_if->true_block);
+ decrease_indent();
- GDScriptWarning warn;
- warn.code = (GDScriptWarning::Code)p_code;
- warn.symbols = p_symbols;
- warn.line = p_line == -1 ? tokenizer->get_token_line() : p_line;
+ // FIXME: Properly detect "elif" blocks.
+ if (p_if->false_block != nullptr) {
+ push_line("Else :");
+ increase_indent();
+ print_suite(p_if->false_block);
+ decrease_indent();
+ }
+}
- List<GDScriptWarning>::Element *before = nullptr;
- for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
- if (E->get().line > warn.line) {
+void GDScriptParser::TreePrinter::print_literal(LiteralNode *p_literal) {
+ // Prefix for string types.
+ switch (p_literal->value.get_type()) {
+ case Variant::NODE_PATH:
+ push_text("^\"");
+ break;
+ case Variant::STRING:
+ push_text("\"");
+ break;
+ case Variant::STRING_NAME:
+ push_text("&\"");
+ break;
+ default:
break;
- }
- before = E;
}
- if (before) {
- warnings.insert_after(before, warn);
- } else {
- warnings.push_front(warn);
+ push_text(p_literal->value);
+ // Suffix for string types.
+ switch (p_literal->value.get_type()) {
+ case Variant::NODE_PATH:
+ case Variant::STRING:
+ case Variant::STRING_NAME:
+ push_text("\"");
+ break;
+ default:
+ break;
}
}
-#endif // DEBUG_ENABLED
-
-String GDScriptParser::get_error() const {
- return error;
-}
-int GDScriptParser::get_error_line() const {
- return error_line;
-}
-
-int GDScriptParser::get_error_column() const {
- return error_column;
-}
+void GDScriptParser::TreePrinter::print_match(MatchNode *p_match) {
+ push_text("Match ");
+ print_expression(p_match->test);
+ push_line(" :");
-bool GDScriptParser::has_error() const {
- return error_set;
+ increase_indent();
+ for (int i = 0; i < p_match->branches.size(); i++) {
+ print_match_branch(p_match->branches[i]);
+ }
+ decrease_indent();
}
-Error GDScriptParser::_parse(const String &p_base_path) {
- base_path = p_base_path;
-
- //assume class
- ClassNode *main_class = alloc_node<ClassNode>();
- main_class->initializer = alloc_node<BlockNode>();
- main_class->initializer->parent_class = main_class;
- main_class->ready = alloc_node<BlockNode>();
- main_class->ready->parent_class = main_class;
- current_class = main_class;
-
- _parse_class(main_class);
-
- if (tokenizer->get_token() == GDScriptTokenizer::TK_ERROR) {
- error_set = false;
- _set_error("Parse error: " + tokenizer->get_token_error());
+void GDScriptParser::TreePrinter::print_match_branch(MatchBranchNode *p_match_branch) {
+ for (int i = 0; i < p_match_branch->patterns.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
+ }
+ print_match_pattern(p_match_branch->patterns[i]);
}
- bool for_completion_error_set = false;
- if (error_set && for_completion) {
- for_completion_error_set = true;
- error_set = false;
- }
+ push_line(" :");
- if (error_set) {
- return ERR_PARSE_ERROR;
- }
+ increase_indent();
+ print_suite(p_match_branch->block);
+ decrease_indent();
+}
- if (dependencies_only) {
- return OK;
+void GDScriptParser::TreePrinter::print_match_pattern(PatternNode *p_match_pattern) {
+ switch (p_match_pattern->pattern_type) {
+ case PatternNode::PT_LITERAL:
+ print_literal(p_match_pattern->literal);
+ break;
+ case PatternNode::PT_WILDCARD:
+ push_text("_");
+ break;
+ case PatternNode::PT_REST:
+ push_text("..");
+ break;
+ case PatternNode::PT_BIND:
+ push_text("Var ");
+ print_identifier(p_match_pattern->bind);
+ break;
+ case PatternNode::PT_EXPRESSION:
+ print_expression(p_match_pattern->expression);
+ break;
+ case PatternNode::PT_ARRAY:
+ push_text("[ ");
+ for (int i = 0; i < p_match_pattern->array.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
+ }
+ print_match_pattern(p_match_pattern->array[i]);
+ }
+ push_text(" ]");
+ break;
+ case PatternNode::PT_DICTIONARY:
+ push_text("{ ");
+ for (int i = 0; i < p_match_pattern->dictionary.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
+ }
+ if (p_match_pattern->dictionary[i].key != nullptr) {
+ // Key can be null for rest pattern.
+ print_expression(p_match_pattern->dictionary[i].key);
+ push_text(" : ");
+ }
+ print_match_pattern(p_match_pattern->dictionary[i].value_pattern);
+ }
+ push_text(" }");
+ break;
}
+}
- _determine_inheritance(main_class);
-
- if (error_set) {
- return ERR_PARSE_ERROR;
+void GDScriptParser::TreePrinter::print_parameter(ParameterNode *p_parameter) {
+ print_identifier(p_parameter->identifier);
+ if (p_parameter->datatype_specifier != nullptr) {
+ push_text(" : ");
+ print_type(p_parameter->datatype_specifier);
}
-
- current_class = main_class;
- current_function = nullptr;
- current_block = nullptr;
-
- if (for_completion) {
- check_types = false;
+ if (p_parameter->default_value != nullptr) {
+ push_text(" = ");
+ print_expression(p_parameter->default_value);
}
+}
- // Resolve all class-level stuff before getting into function blocks
- _check_class_level_types(main_class);
+void GDScriptParser::TreePrinter::print_preload(PreloadNode *p_preload) {
+ push_text(R"(Preload ( ")");
+ push_text(p_preload->resolved_path);
+ push_text(R"(" )");
+}
- if (error_set) {
- return ERR_PARSE_ERROR;
+void GDScriptParser::TreePrinter::print_return(ReturnNode *p_return) {
+ push_text("Return");
+ if (p_return->return_value != nullptr) {
+ push_text(" ");
+ print_expression(p_return->return_value);
}
+ push_line();
+}
- // Resolve the function blocks
- _check_class_blocks_types(main_class);
-
- if (for_completion_error_set) {
- error_set = true;
+void GDScriptParser::TreePrinter::print_self(SelfNode *p_self) {
+ push_text("Self(");
+ if (p_self->current_class->identifier != nullptr) {
+ print_identifier(p_self->current_class->identifier);
+ } else {
+ push_text("<main class>");
}
+ push_text(")");
+}
- if (error_set) {
- return ERR_PARSE_ERROR;
+void GDScriptParser::TreePrinter::print_signal(SignalNode *p_signal) {
+ push_text("Signal ");
+ print_identifier(p_signal->identifier);
+ push_text("( ");
+ for (int i = 0; i < p_signal->parameters.size(); i++) {
+ print_parameter(p_signal->parameters[i]);
}
+ push_line(" )");
+}
-#ifdef DEBUG_ENABLED
-
- // Resolve warning ignores
- Vector<Pair<int, String>> warning_skips = tokenizer->get_warning_skips();
- bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize();
- for (List<GDScriptWarning>::Element *E = warnings.front(); E;) {
- GDScriptWarning &w = E->get();
- int skip_index = -1;
- for (int i = 0; i < warning_skips.size(); i++) {
- if (warning_skips[i].first >= w.line) {
- break;
- }
- skip_index = i;
- }
- List<GDScriptWarning>::Element *next = E->next();
- bool erase = false;
- if (skip_index != -1) {
- if (warning_skips[skip_index].second == GDScriptWarning::get_name_from_code(w.code).to_lower()) {
- erase = true;
- }
- warning_skips.remove(skip_index);
- }
- if (erase) {
- warnings.erase(E);
- } else if (warning_is_error) {
- _set_error(w.get_message() + " (warning treated as error)", w.line);
- return ERR_PARSE_ERROR;
- }
- E = next;
+void GDScriptParser::TreePrinter::print_subscript(SubscriptNode *p_subscript) {
+ print_expression(p_subscript->base);
+ if (p_subscript->is_attribute) {
+ push_text(".");
+ print_identifier(p_subscript->attribute);
+ } else {
+ push_text("[ ");
+ print_expression(p_subscript->index);
+ push_text(" ]");
}
-#endif // DEBUG_ENABLED
-
- return OK;
}
-Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path, const String &p_self_path) {
- clear();
-
- self_path = p_self_path;
- GDScriptTokenizerBuffer *tb = memnew(GDScriptTokenizerBuffer);
- tb->set_code_buffer(p_bytecode);
- tokenizer = tb;
- Error ret = _parse(p_base_path);
- memdelete(tb);
- tokenizer = nullptr;
- return ret;
+void GDScriptParser::TreePrinter::print_statement(Node *p_statement) {
+ switch (p_statement->type) {
+ case Node::ASSERT:
+ print_assert(static_cast<AssertNode *>(p_statement));
+ break;
+ case Node::VARIABLE:
+ print_variable(static_cast<VariableNode *>(p_statement));
+ break;
+ case Node::CONSTANT:
+ print_constant(static_cast<ConstantNode *>(p_statement));
+ break;
+ case Node::IF:
+ print_if(static_cast<IfNode *>(p_statement));
+ break;
+ case Node::FOR:
+ print_for(static_cast<ForNode *>(p_statement));
+ break;
+ case Node::WHILE:
+ print_while(static_cast<WhileNode *>(p_statement));
+ break;
+ case Node::MATCH:
+ print_match(static_cast<MatchNode *>(p_statement));
+ break;
+ case Node::RETURN:
+ print_return(static_cast<ReturnNode *>(p_statement));
+ break;
+ case Node::BREAK:
+ push_line("Break");
+ break;
+ case Node::CONTINUE:
+ push_line("Continue");
+ break;
+ case Node::PASS:
+ push_line("Pass");
+ break;
+ case Node::BREAKPOINT:
+ push_line("Breakpoint");
+ break;
+ case Node::ASSIGNMENT:
+ print_assignment(static_cast<AssignmentNode *>(p_statement));
+ break;
+ default:
+ if (p_statement->is_expression()) {
+ print_expression(static_cast<ExpressionNode *>(p_statement));
+ push_line();
+ } else {
+ push_line(vformat("<unknown statement %d>", p_statement->type));
+ }
+ break;
+ }
}
-Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines, bool p_dependencies_only) {
- clear();
-
- self_path = p_self_path;
- GDScriptTokenizerText *tt = memnew(GDScriptTokenizerText);
- tt->set_code(p_code);
-
- validating = p_just_validate;
- for_completion = p_for_completion;
- dependencies_only = p_dependencies_only;
-#ifdef DEBUG_ENABLED
- safe_lines = r_safe_lines;
-#endif // DEBUG_ENABLED
- tokenizer = tt;
- Error ret = _parse(p_base_path);
- memdelete(tt);
- tokenizer = nullptr;
- return ret;
+void GDScriptParser::TreePrinter::print_suite(SuiteNode *p_suite) {
+ for (int i = 0; i < p_suite->statements.size(); i++) {
+ print_statement(p_suite->statements[i]);
+ }
}
-bool GDScriptParser::is_tool_script() const {
- return (head && head->type == Node::TYPE_CLASS && static_cast<const ClassNode *>(head)->tool);
+void GDScriptParser::TreePrinter::print_ternary_op(TernaryOpNode *p_ternary_op) {
+ // Surround in parenthesis for disambiguation.
+ push_text("(");
+ print_expression(p_ternary_op->true_expr);
+ push_text(") IF (");
+ print_expression(p_ternary_op->condition);
+ push_text(") ELSE (");
+ print_expression(p_ternary_op->false_expr);
+ push_text(")");
}
-const GDScriptParser::Node *GDScriptParser::get_parse_tree() const {
- return head;
+void GDScriptParser::TreePrinter::print_type(TypeNode *p_type) {
+ if (p_type->type_chain.empty()) {
+ push_text("Void");
+ } else {
+ for (int i = 0; i < p_type->type_chain.size(); i++) {
+ if (i > 0) {
+ push_text(".");
+ }
+ print_identifier(p_type->type_chain[i]);
+ }
+ }
}
-void GDScriptParser::clear() {
- while (list) {
- Node *l = list;
- list = list->next;
- memdelete(l);
+void GDScriptParser::TreePrinter::print_unary_op(UnaryOpNode *p_unary_op) {
+ // Surround in parenthesis for disambiguation.
+ push_text("(");
+ switch (p_unary_op->operation) {
+ case UnaryOpNode::OP_POSITIVE:
+ push_text("+");
+ break;
+ case UnaryOpNode::OP_NEGATIVE:
+ push_text("-");
+ break;
+ case UnaryOpNode::OP_LOGIC_NOT:
+ push_text("NOT");
+ break;
+ case UnaryOpNode::OP_COMPLEMENT:
+ push_text("~");
+ break;
}
-
- head = nullptr;
- list = nullptr;
-
- completion_type = COMPLETION_NONE;
- completion_node = nullptr;
- completion_class = nullptr;
- completion_function = nullptr;
- completion_block = nullptr;
- current_block = nullptr;
- current_class = nullptr;
-
- completion_found = false;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
-
- current_function = nullptr;
-
- validating = false;
- for_completion = false;
- error_set = false;
- indent_level.clear();
- indent_level.push_back(IndentLevel(0, 0));
- error_line = 0;
- error_column = 0;
- pending_newline = -1;
- parenthesis = 0;
- current_export.type = Variant::NIL;
- check_types = true;
- dependencies_only = false;
- dependencies.clear();
- error = "";
-#ifdef DEBUG_ENABLED
- safe_lines = nullptr;
-#endif // DEBUG_ENABLED
+ print_expression(p_unary_op->operand);
+ // Surround in parenthesis for disambiguation.
+ push_text(")");
}
-GDScriptParser::CompletionType GDScriptParser::get_completion_type() {
- return completion_type;
-}
+void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
+ for (const List<AnnotationNode *>::Element *E = p_variable->annotations.front(); E != nullptr; E = E->next()) {
+ print_annotation(E->get());
+ }
-StringName GDScriptParser::get_completion_cursor() {
- return completion_cursor;
-}
+ push_text("Variable ");
+ print_identifier(p_variable->identifier);
-int GDScriptParser::get_completion_line() {
- return completion_line;
-}
+ push_text(" : ");
+ if (p_variable->datatype_specifier != nullptr) {
+ print_type(p_variable->datatype_specifier);
+ } else if (p_variable->infer_datatype) {
+ push_text("<inferred type>");
+ } else {
+ push_text("Variant");
+ }
-Variant::Type GDScriptParser::get_completion_built_in_constant() {
- return completion_built_in_constant;
-}
+ increase_indent();
-GDScriptParser::Node *GDScriptParser::get_completion_node() {
- return completion_node;
-}
+ push_line();
+ push_text("= ");
+ if (p_variable->initializer == nullptr) {
+ push_text("<default value>");
+ } else {
+ print_expression(p_variable->initializer);
+ }
+ push_line();
+
+ if (p_variable->property != VariableNode::PROP_NONE) {
+ if (p_variable->getter != nullptr) {
+ push_text("Get");
+ if (p_variable->property == VariableNode::PROP_INLINE) {
+ push_line(":");
+ increase_indent();
+ print_suite(p_variable->getter);
+ decrease_indent();
+ } else {
+ push_line(" =");
+ increase_indent();
+ print_identifier(p_variable->getter_pointer);
+ push_line();
+ decrease_indent();
+ }
+ }
+ if (p_variable->setter != nullptr) {
+ push_text("Set (");
+ if (p_variable->property == VariableNode::PROP_INLINE) {
+ if (p_variable->setter_parameter != nullptr) {
+ print_identifier(p_variable->setter_parameter);
+ } else {
+ push_text("<missing>");
+ }
+ push_line("):");
+ increase_indent();
+ print_suite(p_variable->setter);
+ decrease_indent();
+ } else {
+ push_line(" =");
+ increase_indent();
+ print_identifier(p_variable->setter_pointer);
+ push_line();
+ decrease_indent();
+ }
+ }
+ }
-GDScriptParser::BlockNode *GDScriptParser::get_completion_block() {
- return completion_block;
+ decrease_indent();
+ push_line();
}
-GDScriptParser::ClassNode *GDScriptParser::get_completion_class() {
- return completion_class;
-}
+void GDScriptParser::TreePrinter::print_while(WhileNode *p_while) {
+ push_text("While ");
+ print_expression(p_while->condition);
+ push_line(" :");
-GDScriptParser::FunctionNode *GDScriptParser::get_completion_function() {
- return completion_function;
+ increase_indent();
+ print_suite(p_while->loop);
+ decrease_indent();
}
-int GDScriptParser::get_completion_argument_index() {
- return completion_argument;
-}
+void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) {
+ ERR_FAIL_COND_MSG(p_parser.get_tree() == nullptr, "Parse the code before printing the parse tree.");
-bool GDScriptParser::get_completion_identifier_is_function() {
- return completion_ident_is_call;
-}
+ if (p_parser.is_tool()) {
+ push_line("@tool");
+ }
+ if (!p_parser.get_tree()->icon_path.empty()) {
+ push_text(R"(@icon (")");
+ push_text(p_parser.get_tree()->icon_path);
+ push_line("\")");
+ }
+ print_class(p_parser.get_tree());
-GDScriptParser::GDScriptParser() {
- head = nullptr;
- list = nullptr;
- tokenizer = nullptr;
- pending_newline = -1;
- clear();
+ print_line(printed);
}
-GDScriptParser::~GDScriptParser() {
- clear();
-}
+#endif // DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index cfcca9584e..4c9473c7bd 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -31,663 +31,1324 @@
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
+#include "core/hash_map.h"
+#include "core/io/multiplayer_api.h"
+#include "core/list.h"
#include "core/map.h"
-#include "core/object.h"
+#include "core/reference.h"
+#include "core/resource.h"
#include "core/script_language.h"
+#include "core/string_name.h"
+#include "core/ustring.h"
+#include "core/variant.h"
+#include "core/vector.h"
+#include "gdscript_cache.h"
#include "gdscript_functions.h"
#include "gdscript_tokenizer.h"
-struct GDScriptDataType;
-struct GDScriptWarning;
+#ifdef DEBUG_ENABLED
+#include "core/string_builder.h"
+#include "gdscript_warning.h"
+#endif // DEBUG_ENABLED
class GDScriptParser {
+ struct AnnotationInfo;
+
public:
+ // Forward-declare all parser nodes, to avoid ordering issues.
+ struct AnnotationNode;
+ struct ArrayNode;
+ struct AssertNode;
+ struct AssignmentNode;
+ struct AwaitNode;
+ struct BinaryOpNode;
+ struct BreakNode;
+ struct BreakpointNode;
+ struct CallNode;
+ struct CastNode;
struct ClassNode;
+ struct ConstantNode;
+ struct ContinueNode;
+ struct DictionaryNode;
+ struct EnumNode;
+ struct ExpressionNode;
+ struct ForNode;
+ struct FunctionNode;
+ struct GetNodeNode;
+ struct IdentifierNode;
+ struct IfNode;
+ struct LiteralNode;
+ struct MatchNode;
+ struct MatchBranchNode;
+ struct ParameterNode;
+ struct PassNode;
+ struct PatternNode;
+ struct PreloadNode;
+ struct ReturnNode;
+ struct SelfNode;
+ struct SignalNode;
+ struct SubscriptNode;
+ struct SuiteNode;
+ struct TernaryOpNode;
+ struct TypeNode;
+ struct UnaryOpNode;
+ struct VariableNode;
+ struct WhileNode;
struct DataType {
enum Kind {
BUILTIN,
NATIVE,
SCRIPT,
- GDSCRIPT,
- CLASS,
- UNRESOLVED
+ CLASS, // GDScript.
+ ENUM, // Full enumeration.
+ ENUM_VALUE, // Value from enumeration.
+ VARIANT, // Can be any type.
+ UNRESOLVED,
+ // TODO: Enum
};
-
Kind kind = UNRESOLVED;
- bool has_type = false;
+ enum TypeSource {
+ UNDETECTED, // Can be any type.
+ INFERRED, // Has inferred type, but still dynamic.
+ ANNOTATED_EXPLICIT, // Has a specific type annotated.
+ ANNOTATED_INFERRED, // Has a static type but comes from the assigned value.
+ };
+ TypeSource type_source = UNDETECTED;
+
bool is_constant = false;
- bool is_meta_type = false; // Whether the value can be used as a type
- bool infer_type = false;
- bool may_yield = false; // For function calls
+ bool is_meta_type = false;
+ bool is_coroutine = false; // For function calls.
Variant::Type builtin_type = Variant::NIL;
StringName native_type;
+ StringName enum_type; // Enum name or the value name in an enum.
Ref<Script> script_type;
+ String script_path;
ClassNode *class_type = nullptr;
+ MethodInfo method_info; // For callable/signals.
+ HashMap<StringName, int> enum_values; // For enums.
+
+ _FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
+ _FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
+ _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == UNRESOLVED; }
+ _FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
String to_string() const;
- bool operator==(const DataType &other) const {
- if (!has_type || !other.has_type) {
- return true; // Can be considered equal for parsing purpose
+ bool operator==(const DataType &p_other) const {
+ if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) {
+ return true; // Can be consireded equal for parsing purposes.
}
- if (kind != other.kind) {
+
+ if (type_source == INFERRED || p_other.type_source == INFERRED) {
+ return true; // Can be consireded equal for parsing purposes.
+ }
+
+ if (kind != p_other.kind) {
return false;
}
+
switch (kind) {
- case BUILTIN: {
- return builtin_type == other.builtin_type;
- } break;
- case NATIVE: {
- return native_type == other.native_type;
- } break;
- case GDSCRIPT:
- case SCRIPT: {
- return script_type == other.script_type;
- } break;
- case CLASS: {
- return class_type == other.class_type;
- } break;
- case UNRESOLVED: {
- } break;
+ case VARIANT:
+ return true; // All variants are the same.
+ case BUILTIN:
+ return builtin_type == p_other.builtin_type;
+ case NATIVE:
+ case ENUM:
+ return native_type == p_other.native_type;
+ case ENUM_VALUE:
+ return native_type == p_other.native_type && enum_type == p_other.enum_type;
+ case SCRIPT:
+ return script_type == p_other.script_type;
+ case CLASS:
+ return class_type == p_other.class_type;
+ case UNRESOLVED:
+ break;
}
+
return false;
}
- DataType() {}
+ bool operator!=(const DataType &p_other) const {
+ return !(this->operator==(p_other));
+ }
+ };
+
+ struct ParserError {
+ // TODO: Do I really need a "type"?
+ // enum Type {
+ // NO_ERROR,
+ // EMPTY_FILE,
+ // CLASS_NAME_USED_TWICE,
+ // EXTENDS_USED_TWICE,
+ // EXPECTED_END_STATEMENT,
+ // };
+ // Type type = NO_ERROR;
+ String message;
+ int line = 0, column = 0;
};
struct Node {
enum Type {
- TYPE_CLASS,
- TYPE_FUNCTION,
- TYPE_BUILT_IN_FUNCTION,
- TYPE_BLOCK,
- TYPE_IDENTIFIER,
- TYPE_TYPE,
- TYPE_CONSTANT,
- TYPE_ARRAY,
- TYPE_DICTIONARY,
- TYPE_SELF,
- TYPE_OPERATOR,
- TYPE_CONTROL_FLOW,
- TYPE_LOCAL_VAR,
- TYPE_CAST,
- TYPE_ASSERT,
- TYPE_BREAKPOINT,
- TYPE_NEWLINE,
+ NONE,
+ ANNOTATION,
+ ARRAY,
+ ASSERT,
+ ASSIGNMENT,
+ AWAIT,
+ BINARY_OPERATOR,
+ BREAK,
+ BREAKPOINT,
+ CALL,
+ CAST,
+ CLASS,
+ CONSTANT,
+ CONTINUE,
+ DICTIONARY,
+ ENUM,
+ FOR,
+ FUNCTION,
+ GET_NODE,
+ IDENTIFIER,
+ IF,
+ LITERAL,
+ MATCH,
+ MATCH_BRANCH,
+ PARAMETER,
+ PASS,
+ PATTERN,
+ PRELOAD,
+ RETURN,
+ SELF,
+ SIGNAL,
+ SUBSCRIPT,
+ SUITE,
+ TERNARY_OPERATOR,
+ TYPE,
+ UNARY_OPERATOR,
+ VARIABLE,
+ WHILE,
};
- Node *next;
- int line;
- int column;
- Type type;
+ Type type = NONE;
+ int start_line = 0, end_line = 0;
+ int start_column = 0, end_column = 0;
+ int leftmost_column = 0, rightmost_column = 0;
+ Node *next = nullptr;
+ List<AnnotationNode *> annotations;
- virtual DataType get_datatype() const { return DataType(); }
- virtual void set_datatype(const DataType &p_datatype) {}
+ DataType datatype;
+
+ virtual DataType get_datatype() const { return datatype; }
+ virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+
+ virtual bool is_expression() const { return false; }
virtual ~Node() {}
};
- struct FunctionNode;
- struct BlockNode;
- struct ConstantNode;
- struct LocalVarNode;
- struct OperatorNode;
+ struct ExpressionNode : public Node {
+ // Base type for all expression kinds.
+ bool reduced = false;
+ bool is_constant = false;
+ Variant reduced_value;
- struct ClassNode : public Node {
- bool tool;
+ virtual bool is_expression() const { return true; }
+ virtual ~ExpressionNode() {}
+
+ protected:
+ ExpressionNode() {}
+ };
+
+ struct AnnotationNode : public Node {
StringName name;
- bool extends_used;
- bool classname_used;
- StringName extends_file;
- Vector<StringName> extends_class;
- DataType base_type;
- String icon_path;
+ Vector<ExpressionNode *> arguments;
+ Vector<Variant> resolved_arguments;
- struct Member {
- PropertyInfo _export;
-#ifdef TOOLS_ENABLED
- Variant default_value;
-#endif
- StringName identifier;
- DataType data_type;
- StringName setter;
- StringName getter;
- int line;
- Node *expression;
- OperatorNode *initial_assignment;
- MultiplayerAPI::RPCMode rpc_mode;
- int usages;
- };
+ AnnotationInfo *info = nullptr;
- struct Constant {
- Node *expression;
- DataType type;
- };
+ bool apply(GDScriptParser *p_this, Node *p_target) const;
+ bool applies_to(uint32_t p_target_kinds) const;
- struct Signal {
- StringName name;
- Vector<StringName> arguments;
- int emissions;
- int line;
- };
+ AnnotationNode() {
+ type = ANNOTATION;
+ }
+ };
- Vector<ClassNode *> subclasses;
- Vector<Member> variables;
- Map<StringName, Constant> constant_expressions;
- Vector<FunctionNode *> functions;
- Vector<FunctionNode *> static_functions;
- Vector<Signal> _signals;
- BlockNode *initializer;
- BlockNode *ready;
- ClassNode *owner;
- //Vector<Node*> initializers;
- int end_line;
+ struct ArrayNode : public ExpressionNode {
+ Vector<ExpressionNode *> elements;
- ClassNode() {
- tool = false;
- type = TYPE_CLASS;
- extends_used = false;
- classname_used = false;
- end_line = -1;
- owner = nullptr;
+ ArrayNode() {
+ type = ARRAY;
}
};
- struct FunctionNode : public Node {
- bool _static;
- MultiplayerAPI::RPCMode rpc_mode;
- bool has_yield;
- bool has_unreachable_code;
- StringName name;
- DataType return_type;
- Vector<StringName> arguments;
- Vector<DataType> argument_types;
- Vector<Node *> default_values;
- BlockNode *body;
-#ifdef DEBUG_ENABLED
- Vector<int> arguments_usage;
-#endif // DEBUG_ENABLED
+ struct AssertNode : public Node {
+ ExpressionNode *condition = nullptr;
+ LiteralNode *message = nullptr;
- virtual DataType get_datatype() const { return return_type; }
- virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
- int get_required_argument_count() { return arguments.size() - default_values.size(); }
+ AssertNode() {
+ type = ASSERT;
+ }
+ };
- FunctionNode() {
- type = TYPE_FUNCTION;
- _static = false;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
- has_yield = false;
- has_unreachable_code = false;
+ struct AssignmentNode : public ExpressionNode {
+ // Assignment is not really an expression but it's easier to parse as if it were.
+ enum Operation {
+ OP_NONE,
+ OP_ADDITION,
+ OP_SUBTRACTION,
+ OP_MULTIPLICATION,
+ OP_DIVISION,
+ OP_MODULO,
+ OP_BIT_SHIFT_LEFT,
+ OP_BIT_SHIFT_RIGHT,
+ OP_BIT_AND,
+ OP_BIT_OR,
+ OP_BIT_XOR,
+ };
+
+ Operation operation = OP_NONE;
+ Variant::Operator variant_op = Variant::OP_MAX;
+ ExpressionNode *assignee = nullptr;
+ ExpressionNode *assigned_value = nullptr;
+
+ AssignmentNode() {
+ type = ASSIGNMENT;
}
};
- struct BlockNode : public Node {
- ClassNode *parent_class = nullptr;
- BlockNode *parent_block = nullptr;
- List<Node *> statements;
- Map<StringName, LocalVarNode *> variables;
- bool has_return = false;
+ struct AwaitNode : public ExpressionNode {
+ ExpressionNode *to_await = nullptr;
- Node *if_condition = nullptr; //tiny hack to improve code completion on if () blocks
+ AwaitNode() {
+ type = AWAIT;
+ }
+ };
+
+ struct BinaryOpNode : public ExpressionNode {
+ enum OpType {
+ OP_ADDITION,
+ OP_SUBTRACTION,
+ OP_MULTIPLICATION,
+ OP_DIVISION,
+ OP_MODULO,
+ OP_BIT_LEFT_SHIFT,
+ OP_BIT_RIGHT_SHIFT,
+ OP_BIT_AND,
+ OP_BIT_OR,
+ OP_BIT_XOR,
+ OP_LOGIC_AND,
+ OP_LOGIC_OR,
+ OP_TYPE_TEST,
+ OP_CONTENT_TEST,
+ OP_COMP_EQUAL,
+ OP_COMP_NOT_EQUAL,
+ OP_COMP_LESS,
+ OP_COMP_LESS_EQUAL,
+ OP_COMP_GREATER,
+ OP_COMP_GREATER_EQUAL,
+ };
- //the following is useful for code completion
- List<BlockNode *> sub_blocks;
- int end_line = -1;
+ OpType operation;
+ Variant::Operator variant_op = Variant::OP_MAX;
+ ExpressionNode *left_operand = nullptr;
+ ExpressionNode *right_operand = nullptr;
- BlockNode() {
- type = TYPE_BLOCK;
+ BinaryOpNode() {
+ type = BINARY_OPERATOR;
}
};
- struct TypeNode : public Node {
- Variant::Type vtype;
+ struct BreakNode : public Node {
+ BreakNode() {
+ type = BREAK;
+ }
+ };
- TypeNode() {
- type = TYPE_TYPE;
+ struct BreakpointNode : public Node {
+ BreakpointNode() {
+ type = BREAKPOINT;
+ }
+ };
+
+ struct CallNode : public ExpressionNode {
+ ExpressionNode *callee = nullptr;
+ Vector<ExpressionNode *> arguments;
+ StringName function_name;
+ bool is_super = false;
+
+ CallNode() {
+ type = CALL;
+ }
+
+ Type get_callee_type() const {
+ if (callee == nullptr) {
+ return Type::NONE;
+ } else {
+ return callee->type;
+ }
}
};
- struct BuiltInFunctionNode : public Node {
- GDScriptFunctions::Function function;
+ struct CastNode : public ExpressionNode {
+ ExpressionNode *operand = nullptr;
+ TypeNode *cast_type = nullptr;
- BuiltInFunctionNode() {
- type = TYPE_BUILT_IN_FUNCTION;
+ CastNode() {
+ type = CAST;
}
};
- struct IdentifierNode : public Node {
- StringName name;
- BlockNode *declared_block = nullptr; // Simplify lookup by checking if it is declared locally
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ struct EnumNode : public Node {
+ struct Value {
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *custom_value = nullptr;
+ EnumNode *parent_enum = nullptr;
+ int index = -1;
+ bool resolved = false;
+ int value = 0;
+ int line = 0;
+ int leftmost_column = 0;
+ int rightmost_column = 0;
+ };
+ IdentifierNode *identifier = nullptr;
+ Vector<Value> values;
- IdentifierNode() {
- type = TYPE_IDENTIFIER;
+ EnumNode() {
+ type = ENUM;
}
};
- struct LocalVarNode : public Node {
- StringName name;
- Node *assign = nullptr;
- OperatorNode *assign_op = nullptr;
- int assignments = 0;
- int usages = 0;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ struct ClassNode : public Node {
+ struct Member {
+ enum Type {
+ UNDEFINED,
+ CLASS,
+ CONSTANT,
+ FUNCTION,
+ SIGNAL,
+ VARIABLE,
+ ENUM,
+ ENUM_VALUE, // For unnamed enums.
+ };
+
+ Type type = UNDEFINED;
+
+ union {
+ ClassNode *m_class = nullptr;
+ ConstantNode *constant;
+ FunctionNode *function;
+ SignalNode *signal;
+ VariableNode *variable;
+ EnumNode *m_enum;
+ };
+ EnumNode::Value enum_value;
+
+ String get_type_name() const {
+ switch (type) {
+ case UNDEFINED:
+ return "???";
+ case CLASS:
+ return "class";
+ case CONSTANT:
+ return "constant";
+ case FUNCTION:
+ return "function";
+ case SIGNAL:
+ return "signal";
+ case VARIABLE:
+ return "variable";
+ case ENUM:
+ return "enum";
+ case ENUM_VALUE:
+ return "enum value";
+ }
+ return "";
+ }
+
+ int get_line() const {
+ switch (type) {
+ case CLASS:
+ return m_class->start_line;
+ case CONSTANT:
+ return constant->start_line;
+ case FUNCTION:
+ return function->start_line;
+ case VARIABLE:
+ return variable->start_line;
+ case ENUM_VALUE:
+ return enum_value.line;
+ case ENUM:
+ return m_enum->start_line;
+ case SIGNAL:
+ return signal->start_line;
+ case UNDEFINED:
+ ERR_FAIL_V_MSG(-1, "Reaching undefined member type.");
+ }
+ ERR_FAIL_V_MSG(-1, "Reaching unhandled type.");
+ }
+
+ DataType get_datatype() const {
+ switch (type) {
+ case CLASS:
+ return m_class->get_datatype();
+ case CONSTANT:
+ return constant->get_datatype();
+ case FUNCTION:
+ return function->get_datatype();
+ case VARIABLE:
+ return variable->get_datatype();
+ case ENUM:
+ return m_enum->get_datatype();
+ case ENUM_VALUE: {
+ // Always integer.
+ DataType type;
+ type.type_source = DataType::ANNOTATED_EXPLICIT;
+ type.kind = DataType::BUILTIN;
+ type.builtin_type = Variant::INT;
+ return type;
+ }
+ case SIGNAL: {
+ DataType type;
+ type.type_source = DataType::ANNOTATED_EXPLICIT;
+ type.kind = DataType::BUILTIN;
+ type.builtin_type = Variant::SIGNAL;
+ // TODO: Add parameter info.
+ return type;
+ }
+ case UNDEFINED:
+ return DataType();
+ }
+ ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type.");
+ }
+
+ Member() {}
+
+ Member(ClassNode *p_class) {
+ type = CLASS;
+ m_class = p_class;
+ }
+ Member(ConstantNode *p_constant) {
+ type = CONSTANT;
+ constant = p_constant;
+ }
+ Member(VariableNode *p_variable) {
+ type = VARIABLE;
+ variable = p_variable;
+ }
+ Member(SignalNode *p_signal) {
+ type = SIGNAL;
+ signal = p_signal;
+ }
+ Member(FunctionNode *p_function) {
+ type = FUNCTION;
+ function = p_function;
+ }
+ Member(EnumNode *p_enum) {
+ type = ENUM;
+ m_enum = p_enum;
+ }
+ Member(const EnumNode::Value &p_enum_value) {
+ type = ENUM_VALUE;
+ enum_value = p_enum_value;
+ }
+ };
- LocalVarNode() {
- type = TYPE_LOCAL_VAR;
+ IdentifierNode *identifier = nullptr;
+ String icon_path;
+ Vector<Member> members;
+ HashMap<StringName, int> members_indices;
+ ClassNode *outer = nullptr;
+ bool extends_used = false;
+ bool onready_used = false;
+ String extends_path;
+ Vector<StringName> extends; // List for indexing: extends A.B.C
+ DataType base_type;
+ String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project.
+
+ bool resolved_interface = false;
+ bool resolved_body = false;
+
+ Member get_member(const StringName &p_name) const {
+ return members[members_indices[p_name]];
+ }
+ bool has_member(const StringName &p_name) const {
+ return members_indices.has(p_name);
+ }
+ bool has_function(const StringName &p_name) const {
+ return has_member(p_name) && members[members_indices[p_name]].type == Member::FUNCTION;
+ }
+ template <class T>
+ void add_member(T *p_member_node) {
+ members_indices[p_member_node->identifier->name] = members.size();
+ members.push_back(Member(p_member_node));
+ }
+ void add_member(const EnumNode::Value &p_enum_value) {
+ members_indices[p_enum_value.identifier->name] = members.size();
+ members.push_back(Member(p_enum_value));
+ }
+
+ ClassNode() {
+ type = CLASS;
}
};
struct ConstantNode : public Node {
- Variant value;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *initializer = nullptr;
+ TypeNode *datatype_specifier = nullptr;
+ bool infer_datatype = false;
+ int usages = 0;
ConstantNode() {
- type = TYPE_CONSTANT;
+ type = CONSTANT;
}
};
- struct ArrayNode : public Node {
- Vector<Node *> elements;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
-
- ArrayNode() {
- type = TYPE_ARRAY;
- datatype.has_type = true;
- datatype.kind = DataType::BUILTIN;
- datatype.builtin_type = Variant::ARRAY;
+ struct ContinueNode : public Node {
+ bool is_for_match = false;
+ ContinueNode() {
+ type = CONTINUE;
}
};
- struct DictionaryNode : public Node {
+ struct DictionaryNode : public ExpressionNode {
struct Pair {
- Node *key;
- Node *value;
+ ExpressionNode *key = nullptr;
+ ExpressionNode *value = nullptr;
};
-
Vector<Pair> elements;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
+
+ enum Style {
+ LUA_TABLE,
+ PYTHON_DICT,
+ };
+ Style style = PYTHON_DICT;
DictionaryNode() {
- type = TYPE_DICTIONARY;
- datatype.has_type = true;
- datatype.kind = DataType::BUILTIN;
- datatype.builtin_type = Variant::DICTIONARY;
+ type = DICTIONARY;
}
};
- struct SelfNode : public Node {
- SelfNode() {
- type = TYPE_SELF;
- }
- };
-
- struct OperatorNode : public Node {
- enum Operator {
- //call/constructor operator
- OP_CALL,
- OP_PARENT_CALL,
- OP_YIELD,
- OP_IS,
- OP_IS_BUILTIN,
- //indexing operator
- OP_INDEX,
- OP_INDEX_NAMED,
- //unary operators
- OP_NEG,
- OP_POS,
- OP_NOT,
- OP_BIT_INVERT,
- //binary operators (in precedence order)
- OP_IN,
- OP_EQUAL,
- OP_NOT_EQUAL,
- OP_LESS,
- OP_LESS_EQUAL,
- OP_GREATER,
- OP_GREATER_EQUAL,
- OP_AND,
- OP_OR,
- OP_ADD,
- OP_SUB,
- OP_MUL,
- OP_DIV,
- OP_MOD,
- OP_SHIFT_LEFT,
- OP_SHIFT_RIGHT,
- OP_INIT_ASSIGN,
- OP_ASSIGN,
- OP_ASSIGN_ADD,
- OP_ASSIGN_SUB,
- OP_ASSIGN_MUL,
- OP_ASSIGN_DIV,
- OP_ASSIGN_MOD,
- OP_ASSIGN_SHIFT_LEFT,
- OP_ASSIGN_SHIFT_RIGHT,
- OP_ASSIGN_BIT_AND,
- OP_ASSIGN_BIT_OR,
- OP_ASSIGN_BIT_XOR,
- OP_BIT_AND,
- OP_BIT_OR,
- OP_BIT_XOR,
- //ternary operators
- OP_TERNARY_IF,
- OP_TERNARY_ELSE,
- };
+ struct ForNode : public Node {
+ IdentifierNode *variable = nullptr;
+ ExpressionNode *list = nullptr;
+ SuiteNode *loop = nullptr;
- Operator op;
+ ForNode() {
+ type = FOR;
+ }
+ };
- Vector<Node *> arguments;
- DataType datatype;
- virtual DataType get_datatype() const { return datatype; }
- virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
- OperatorNode() {
- type = TYPE_OPERATOR;
+ struct FunctionNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ Vector<ParameterNode *> parameters;
+ HashMap<StringName, int> parameters_indices;
+ TypeNode *return_type = nullptr;
+ SuiteNode *body = nullptr;
+ bool is_static = false;
+ bool is_coroutine = false;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ MethodInfo info;
+
+ bool resolved_signature = false;
+ bool resolved_body = false;
+
+ FunctionNode() {
+ type = FUNCTION;
}
};
- struct PatternNode : public Node {
- enum PatternType {
- PT_CONSTANT,
- PT_BIND,
- PT_DICTIONARY,
- PT_ARRAY,
- PT_IGNORE_REST,
- PT_WILDCARD
+ struct GetNodeNode : public ExpressionNode {
+ LiteralNode *string = nullptr;
+ Vector<IdentifierNode *> chain;
+
+ GetNodeNode() {
+ type = GET_NODE;
+ }
+ };
+
+ struct IdentifierNode : public ExpressionNode {
+ StringName name;
+
+ enum Source {
+ UNDEFINED_SOURCE,
+ FUNCTION_PARAMETER,
+ LOCAL_CONSTANT,
+ LOCAL_VARIABLE,
+ LOCAL_ITERATOR, // `for` loop iterator.
+ LOCAL_BIND, // Pattern bind.
+ MEMBER_VARIABLE,
+ MEMBER_CONSTANT,
};
+ Source source = UNDEFINED_SOURCE;
- PatternType pt_type;
+ union {
+ ParameterNode *parameter_source = nullptr;
+ ConstantNode *constant_source;
+ VariableNode *variable_source;
+ IdentifierNode *bind_source;
+ };
- Node *constant;
- StringName bind;
- Map<ConstantNode *, PatternNode *> dictionary;
- Vector<PatternNode *> array;
+ int usages = 0; // Useful for binds/iterator variable.
+
+ IdentifierNode() {
+ type = IDENTIFIER;
+ }
};
- struct PatternBranchNode : public Node {
- Vector<PatternNode *> patterns;
- BlockNode *body;
+ struct IfNode : public Node {
+ ExpressionNode *condition = nullptr;
+ SuiteNode *true_block = nullptr;
+ SuiteNode *false_block = nullptr;
+
+ IfNode() {
+ type = IF;
+ }
+ };
+
+ struct LiteralNode : public ExpressionNode {
+ Variant value;
+
+ LiteralNode() {
+ type = LITERAL;
+ }
};
struct MatchNode : public Node {
- Node *val_to_match;
- Vector<PatternBranchNode *> branches;
+ ExpressionNode *test = nullptr;
+ Vector<MatchBranchNode *> branches;
- struct CompiledPatternBranch {
- Node *compiled_pattern;
- BlockNode *body;
- };
+ MatchNode() {
+ type = MATCH;
+ }
+ };
- Vector<CompiledPatternBranch> compiled_pattern_branches;
+ struct MatchBranchNode : public Node {
+ Vector<PatternNode *> patterns;
+ SuiteNode *block;
+ bool has_wildcard = false;
+
+ MatchBranchNode() {
+ type = MATCH_BRANCH;
+ }
};
- struct ControlFlowNode : public Node {
- enum CFType {
- CF_IF,
- CF_FOR,
- CF_WHILE,
- CF_BREAK,
- CF_CONTINUE,
- CF_RETURN,
- CF_MATCH
+ struct ParameterNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *default_value = nullptr;
+ TypeNode *datatype_specifier = nullptr;
+ bool infer_datatype = false;
+ int usages = 0;
+
+ ParameterNode() {
+ type = PARAMETER;
+ }
+ };
+
+ struct PassNode : public Node {
+ PassNode() {
+ type = PASS;
+ }
+ };
+
+ struct PatternNode : public Node {
+ enum Type {
+ PT_LITERAL,
+ PT_EXPRESSION,
+ PT_BIND,
+ PT_ARRAY,
+ PT_DICTIONARY,
+ PT_REST,
+ PT_WILDCARD,
};
+ Type pattern_type = PT_LITERAL;
- CFType cf_type = CF_IF;
- Vector<Node *> arguments;
- BlockNode *body = nullptr;
- BlockNode *body_else = nullptr;
+ union {
+ LiteralNode *literal = nullptr;
+ IdentifierNode *bind;
+ ExpressionNode *expression;
+ };
+ Vector<PatternNode *> array;
+ bool rest_used = false; // For array/dict patterns.
- MatchNode *match;
+ struct Pair {
+ ExpressionNode *key = nullptr;
+ PatternNode *value_pattern = nullptr;
+ };
+ Vector<Pair> dictionary;
- ControlFlowNode *_else; //used for if
+ HashMap<StringName, IdentifierNode *> binds;
- ControlFlowNode() {
- type = TYPE_CONTROL_FLOW;
+ bool has_bind(const StringName &p_name);
+ IdentifierNode *get_bind(const StringName &p_name);
+
+ PatternNode() {
+ type = PATTERN;
}
};
+ struct PreloadNode : public ExpressionNode {
+ ExpressionNode *path = nullptr;
+ String resolved_path;
+ Ref<Resource> resource;
- struct CastNode : public Node {
- Node *source_node;
- DataType cast_type;
- DataType return_type;
- virtual DataType get_datatype() const { return return_type; }
- virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
-
- CastNode() {
- type = TYPE_CAST;
+ PreloadNode() {
+ type = PRELOAD;
}
};
- struct AssertNode : public Node {
- Node *condition = nullptr;
- Node *message = nullptr;
+ struct ReturnNode : public Node {
+ ExpressionNode *return_value = nullptr;
- AssertNode() {
- type = TYPE_ASSERT;
+ ReturnNode() {
+ type = RETURN;
}
};
- struct BreakpointNode : public Node {
- BreakpointNode() {
- type = TYPE_BREAKPOINT;
+ struct SelfNode : public ExpressionNode {
+ ClassNode *current_class = nullptr;
+
+ SelfNode() {
+ type = SELF;
}
};
- struct NewLineNode : public Node {
- NewLineNode() {
- type = TYPE_NEWLINE;
+ struct SignalNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ Vector<ParameterNode *> parameters;
+ HashMap<StringName, int> parameters_indices;
+
+ SignalNode() {
+ type = SIGNAL;
}
};
- struct Expression {
- bool is_op;
+ struct SubscriptNode : public ExpressionNode {
+ ExpressionNode *base = nullptr;
union {
- OperatorNode::Operator op;
- Node *node;
+ ExpressionNode *index = nullptr;
+ IdentifierNode *attribute;
};
- };
- enum CompletionType {
- COMPLETION_NONE,
- COMPLETION_BUILT_IN_TYPE_CONSTANT,
- COMPLETION_GET_NODE,
- COMPLETION_FUNCTION,
- COMPLETION_IDENTIFIER,
- COMPLETION_PARENT_FUNCTION,
- COMPLETION_METHOD,
- COMPLETION_CALL_ARGUMENTS,
- COMPLETION_RESOURCE_PATH,
- COMPLETION_INDEX,
- COMPLETION_VIRTUAL_FUNC,
- COMPLETION_YIELD,
- COMPLETION_ASSIGN,
- COMPLETION_TYPE_HINT,
- COMPLETION_TYPE_HINT_INDEX,
+ bool is_attribute = false;
+
+ SubscriptNode() {
+ type = SUBSCRIPT;
+ }
};
-private:
- GDScriptTokenizer *tokenizer;
+ struct SuiteNode : public Node {
+ SuiteNode *parent_block = nullptr;
+ Vector<Node *> statements;
+ struct Local {
+ enum Type {
+ UNDEFINED,
+ CONSTANT,
+ VARIABLE,
+ PARAMETER,
+ FOR_VARIABLE,
+ PATTERN_BIND,
+ };
+ Type type = UNDEFINED;
+ union {
+ ConstantNode *constant = nullptr;
+ VariableNode *variable;
+ ParameterNode *parameter;
+ IdentifierNode *bind;
+ };
+ StringName name;
- Node *head;
- Node *list;
- template <class T>
- T *alloc_node();
-
- bool validating;
- bool for_completion;
- int parenthesis;
- bool error_set;
- String error;
- int error_line;
- int error_column;
- bool check_types;
- bool dependencies_only;
- List<String> dependencies;
-#ifdef DEBUG_ENABLED
- Set<int> *safe_lines;
-#endif // DEBUG_ENABLED
+ int start_line = 0, end_line = 0;
+ int start_column = 0, end_column = 0;
+ int leftmost_column = 0, rightmost_column = 0;
+
+ DataType get_datatype() const;
+ String get_name() const;
+
+ Local() {}
+ Local(ConstantNode *p_constant) {
+ type = CONSTANT;
+ constant = p_constant;
+ name = p_constant->identifier->name;
+
+ start_line = p_constant->start_line;
+ end_line = p_constant->end_line;
+ start_column = p_constant->start_column;
+ end_column = p_constant->end_column;
+ leftmost_column = p_constant->leftmost_column;
+ rightmost_column = p_constant->rightmost_column;
+ }
+ Local(VariableNode *p_variable) {
+ type = VARIABLE;
+ variable = p_variable;
+ name = p_variable->identifier->name;
+
+ start_line = p_variable->start_line;
+ end_line = p_variable->end_line;
+ start_column = p_variable->start_column;
+ end_column = p_variable->end_column;
+ leftmost_column = p_variable->leftmost_column;
+ rightmost_column = p_variable->rightmost_column;
+ }
+ Local(ParameterNode *p_parameter) {
+ type = PARAMETER;
+ parameter = p_parameter;
+ name = p_parameter->identifier->name;
+
+ start_line = p_parameter->start_line;
+ end_line = p_parameter->end_line;
+ start_column = p_parameter->start_column;
+ end_column = p_parameter->end_column;
+ leftmost_column = p_parameter->leftmost_column;
+ rightmost_column = p_parameter->rightmost_column;
+ }
+ Local(IdentifierNode *p_identifier) {
+ type = FOR_VARIABLE;
+ bind = p_identifier;
+ name = p_identifier->name;
+
+ start_line = p_identifier->start_line;
+ end_line = p_identifier->end_line;
+ start_column = p_identifier->start_column;
+ end_column = p_identifier->end_column;
+ leftmost_column = p_identifier->leftmost_column;
+ rightmost_column = p_identifier->rightmost_column;
+ }
+ };
+ Local empty;
+ Vector<Local> locals;
+ HashMap<StringName, int> locals_indices;
-#ifdef DEBUG_ENABLED
- List<GDScriptWarning> warnings;
-#endif // DEBUG_ENABLED
+ FunctionNode *parent_function = nullptr;
+ ForNode *parent_for = nullptr;
+ IfNode *parent_if = nullptr;
+
+ bool has_return = false;
+ bool has_continue = false;
+ bool has_unreachable_code = false; // Just so warnings aren't given more than once per block.
+
+ bool has_local(const StringName &p_name) const;
+ const Local &get_local(const StringName &p_name) const;
+ template <class T>
+ void add_local(T *p_local) {
+ locals_indices[p_local->identifier->name] = locals.size();
+ locals.push_back(Local(p_local));
+ }
+ void add_local(const Local &p_local) {
+ locals_indices[p_local.name] = locals.size();
+ locals.push_back(p_local);
+ }
- int pending_newline;
+ SuiteNode() {
+ type = SUITE;
+ }
+ };
- struct IndentLevel {
- int indent = 0;
- int tabs = 0;
+ struct TernaryOpNode : public ExpressionNode {
+ // Only one ternary operation exists, so no abstraction here.
+ ExpressionNode *condition = nullptr;
+ ExpressionNode *true_expr = nullptr;
+ ExpressionNode *false_expr = nullptr;
- bool is_mixed(IndentLevel other) {
- return (
- (indent == other.indent && tabs != other.tabs) ||
- (indent > other.indent && tabs < other.tabs) ||
- (indent < other.indent && tabs > other.tabs));
+ TernaryOpNode() {
+ type = TERNARY_OPERATOR;
}
+ };
- IndentLevel() {}
+ struct TypeNode : public Node {
+ Vector<IdentifierNode *> type_chain;
- IndentLevel(int p_indent, int p_tabs) :
- indent(p_indent),
- tabs(p_tabs) {}
+ TypeNode() {
+ type = TYPE;
+ }
};
- List<IndentLevel> indent_level;
+ struct UnaryOpNode : public ExpressionNode {
+ enum OpType {
+ OP_POSITIVE,
+ OP_NEGATIVE,
+ OP_COMPLEMENT,
+ OP_LOGIC_NOT,
+ };
- String base_path;
- String self_path;
+ OpType operation;
+ Variant::Operator variant_op = Variant::OP_MAX;
+ ExpressionNode *operand = nullptr;
- ClassNode *current_class;
- FunctionNode *current_function;
- BlockNode *current_block;
+ UnaryOpNode() {
+ type = UNARY_OPERATOR;
+ }
+ };
- bool _get_completable_identifier(CompletionType p_type, StringName &identifier);
- void _make_completable_call(int p_arg);
+ struct VariableNode : public Node {
+ enum PropertyStyle {
+ PROP_NONE,
+ PROP_INLINE,
+ PROP_SETGET,
+ };
- CompletionType completion_type;
- StringName completion_cursor;
- Variant::Type completion_built_in_constant;
- Node *completion_node;
- ClassNode *completion_class;
- FunctionNode *completion_function;
- BlockNode *completion_block;
- int completion_line;
- int completion_argument;
- bool completion_found;
- bool completion_ident_is_call;
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *initializer = nullptr;
+ TypeNode *datatype_specifier = nullptr;
+ bool infer_datatype = false;
- PropertyInfo current_export;
+ PropertyStyle property = PROP_NONE;
+ union {
+ SuiteNode *setter = nullptr;
+ IdentifierNode *setter_pointer;
+ };
+ IdentifierNode *setter_parameter = nullptr;
+ union {
+ SuiteNode *getter = nullptr;
+ IdentifierNode *getter_pointer;
+ };
- MultiplayerAPI::RPCMode rpc_mode;
+ bool exported = false;
+ bool onready = false;
+ PropertyInfo export_info;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ int assignments = 0;
+ int usages = 0;
- void _set_error(const String &p_error, int p_line = -1, int p_column = -1);
-#ifdef DEBUG_ENABLED
- void _add_warning(int p_code, int p_line = -1, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String());
- void _add_warning(int p_code, int p_line, const Vector<String> &p_symbols);
-#endif // DEBUG_ENABLED
- bool _recover_from_completion();
-
- bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false, bool p_parsing_constant = false);
- bool _enter_indent_block(BlockNode *p_block = nullptr);
- bool _parse_newline();
- Node *_parse_expression(Node *p_parent, bool p_static, bool p_allow_assign = false, bool p_parsing_constant = false);
- Node *_reduce_expression(Node *p_node, bool p_to_const = false);
- Node *_parse_and_reduce_expression(Node *p_parent, bool p_static, bool p_reduce_const = false, bool p_allow_assign = false);
- bool _reduce_export_var_type(Variant &p_value, int p_line = 0);
-
- PatternNode *_parse_pattern(bool p_static);
- void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
- void _transform_match_statment(MatchNode *p_match_statement);
- void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings);
-
- void _parse_block(BlockNode *p_block, bool p_static);
- void _parse_extends(ClassNode *p_class);
- void _parse_class(ClassNode *p_class);
- bool _end_statement();
- void _set_end_statement_error(String p_name);
-
- void _determine_inheritance(ClassNode *p_class, bool p_recursive = true);
- bool _parse_type(DataType &r_type, bool p_can_be_void = false);
- DataType _resolve_type(const DataType &p_source, int p_line);
- DataType _type_from_variant(const Variant &p_value) const;
- DataType _type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant = true) const;
- DataType _type_from_gdtype(const GDScriptDataType &p_gdtype) const;
- DataType _get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const;
- Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const;
- bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const;
- bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type, bool *r_is_const = nullptr) const;
- bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const;
- Node *_get_default_value_for_type(const DataType &p_type, int p_line = -1);
-
- DataType _reduce_node_type(Node *p_node);
- DataType _reduce_function_call_type(const OperatorNode *p_call);
- DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line, bool p_is_indexing);
- void _check_class_level_types(ClassNode *p_class);
- void _check_class_blocks_types(ClassNode *p_class);
- void _check_function_types(FunctionNode *p_function);
- void _check_block_types(BlockNode *p_block);
- _FORCE_INLINE_ void _mark_line_as_safe(int p_line) const {
-#ifdef DEBUG_ENABLED
- if (safe_lines) {
- safe_lines->insert(p_line);
+ VariableNode() {
+ type = VARIABLE;
}
-#endif // DEBUG_ENABLED
- }
- _FORCE_INLINE_ void _mark_line_as_unsafe(int p_line) const {
-#ifdef DEBUG_ENABLED
- if (safe_lines) {
- safe_lines->erase(p_line);
+ };
+
+ struct WhileNode : public Node {
+ ExpressionNode *condition = nullptr;
+ SuiteNode *loop = nullptr;
+
+ WhileNode() {
+ type = WHILE;
}
-#endif // DEBUG_ENABLED
- }
+ };
- Error _parse(const String &p_base_path);
+ enum CompletionType {
+ COMPLETION_NONE,
+ COMPLETION_ANNOTATION, // Annotation (following @).
+ COMPLETION_ANNOTATION_ARGUMENTS, // Annotation arguments hint.
+ COMPLETION_ASSIGN, // Assignment based on type (e.g. enum values).
+ COMPLETION_ATTRIBUTE, // After id.| to look for members.
+ COMPLETION_ATTRIBUTE_METHOD, // After id.| to look for methods.
+ COMPLETION_BUILT_IN_TYPE_CONSTANT, // Constants inside a built-in type (e.g. Color.blue).
+ COMPLETION_CALL_ARGUMENTS, // Complete with nodes, input actions, enum values (or usual expressions).
+ // TODO: COMPLETION_DECLARATION, // Potential declaration (var, const, func).
+ COMPLETION_GET_NODE, // Get node with $ notation.
+ COMPLETION_IDENTIFIER, // List available identifiers in scope.
+ COMPLETION_INHERIT_TYPE, // Type after extends. Exclude non-viable types (built-ins, enums, void). Includes subtypes using the argument index.
+ COMPLETION_METHOD, // List available methods in scope.
+ COMPLETION_OVERRIDE_METHOD, // Override implementation, also for native virtuals.
+ COMPLETION_PROPERTY_DECLARATION, // Property declaration (get, set).
+ COMPLETION_PROPERTY_DECLARATION_OR_TYPE, // Property declaration (get, set) or a type hint.
+ COMPLETION_PROPERTY_METHOD, // Property setter or getter (list available methods).
+ COMPLETION_RESOURCE_PATH, // For load/preload.
+ COMPLETION_SUBSCRIPT, // Inside id[|].
+ COMPLETION_SUPER_METHOD, // After super.
+ COMPLETION_TYPE_ATTRIBUTE, // Attribute in type name (Type.|).
+ COMPLETION_TYPE_NAME, // Name of type (after :).
+ COMPLETION_TYPE_NAME_OR_VOID, // Same as TYPE_NAME, but allows void (in function return type).
+ };
-public:
- bool has_error() const;
- String get_error() const;
- int get_error_line() const;
- int get_error_column() const;
+ struct CompletionContext {
+ CompletionType type = COMPLETION_NONE;
+ ClassNode *current_class = nullptr;
+ FunctionNode *current_function = nullptr;
+ SuiteNode *current_suite = nullptr;
+ int current_line = -1;
+ int current_argument = -1;
+ Variant::Type builtin_type = Variant::VARIANT_MAX;
+ Node *node = nullptr;
+ Object *base = nullptr;
+ List<Ref<GDScriptParserRef>> dependent_parsers;
+ };
+
+ struct CompletionCall {
+ Node *call = nullptr;
+ int argument = -1;
+ };
+
+private:
+ friend class GDScriptAnalyzer;
+
+ bool _is_tool = false;
+ String script_path;
+ bool for_completion = false;
+ bool panic_mode = false;
+ bool can_break = false;
+ bool can_continue = false;
+ bool is_continue_match = false; // Whether a `continue` will act on a `match`.
+ bool is_ignoring_warnings = false;
+ List<bool> multiline_stack;
+
+ ClassNode *head = nullptr;
+ Node *list = nullptr;
+ List<ParserError> errors;
#ifdef DEBUG_ENABLED
- const List<GDScriptWarning> &get_warnings() const { return warnings; }
-#endif // DEBUG_ENABLED
- Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = nullptr, bool p_dependencies_only = false);
- Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
+ List<GDScriptWarning> warnings;
+ Set<String> ignored_warnings;
+ Set<int> unsafe_lines;
+#endif
- bool is_tool_script() const;
- const Node *get_parse_tree() const;
+ GDScriptTokenizer tokenizer;
+ GDScriptTokenizer::Token previous;
+ GDScriptTokenizer::Token current;
+
+ ClassNode *current_class = nullptr;
+ FunctionNode *current_function = nullptr;
+ SuiteNode *current_suite = nullptr;
+
+ CompletionContext completion_context;
+ CompletionCall completion_call;
+ List<CompletionCall> completion_call_stack;
+ bool passed_cursor = false;
+
+ typedef bool (GDScriptParser::*AnnotationAction)(const AnnotationNode *p_annotation, Node *p_target);
+ struct AnnotationInfo {
+ enum TargetKind {
+ NONE = 0,
+ SCRIPT = 1 << 0,
+ CLASS = 1 << 1,
+ VARIABLE = 1 << 2,
+ CONSTANT = 1 << 3,
+ SIGNAL = 1 << 4,
+ FUNCTION = 1 << 5,
+ STATEMENT = 1 << 6,
+ CLASS_LEVEL = CLASS | VARIABLE | FUNCTION,
+ };
+ uint32_t target_kind = 0; // Flags.
+ AnnotationAction apply = nullptr;
+ MethodInfo info;
+ };
+ HashMap<StringName, AnnotationInfo> valid_annotations;
+ List<AnnotationNode *> annotation_stack;
+
+ typedef ExpressionNode *(GDScriptParser::*ParseFunction)(ExpressionNode *p_previous_operand, bool p_can_assign);
+ // Higher value means higher precedence (i.e. is evaluated first).
+ enum Precedence {
+ PREC_NONE,
+ PREC_ASSIGNMENT,
+ PREC_CAST,
+ PREC_TERNARY,
+ PREC_LOGIC_OR,
+ PREC_LOGIC_AND,
+ PREC_LOGIC_NOT,
+ PREC_CONTENT_TEST,
+ PREC_COMPARISON,
+ PREC_BIT_OR,
+ PREC_BIT_XOR,
+ PREC_BIT_AND,
+ PREC_BIT_SHIFT,
+ PREC_SUBTRACTION,
+ PREC_ADDITION,
+ PREC_FACTOR,
+ PREC_SIGN,
+ PREC_BIT_NOT,
+ PREC_TYPE_TEST,
+ PREC_AWAIT,
+ PREC_CALL,
+ PREC_ATTRIBUTE,
+ PREC_SUBSCRIPT,
+ PREC_PRIMARY,
+ };
+ struct ParseRule {
+ ParseFunction prefix = nullptr;
+ ParseFunction infix = nullptr;
+ Precedence precedence = PREC_NONE;
+ };
+ static ParseRule *get_rule(GDScriptTokenizer::Token::Type p_token_type);
- //completion info
+ template <class T>
+ T *alloc_node() {
+ T *node = memnew(T);
- CompletionType get_completion_type();
- StringName get_completion_cursor();
- int get_completion_line();
- Variant::Type get_completion_built_in_constant();
- Node *get_completion_node();
- ClassNode *get_completion_class();
- BlockNode *get_completion_block();
- FunctionNode *get_completion_function();
- int get_completion_argument_index();
- bool get_completion_identifier_is_function();
+ node->next = list;
+ list = node;
- const List<String> &get_dependencies() const { return dependencies; }
+ // TODO: Properly set positions for all nodes.
+ node->start_line = previous.start_line;
+ node->end_line = previous.end_line;
+ node->start_column = previous.start_column;
+ node->end_column = previous.end_column;
+ node->leftmost_column = previous.leftmost_column;
+ node->rightmost_column = previous.rightmost_column;
+ return node;
+ }
void clear();
+ void push_error(const String &p_message, const Node *p_origin = nullptr);
+#ifdef DEBUG_ENABLED
+ void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String());
+ void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols);
+#endif
+
+ void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false);
+ void make_completion_context(CompletionType p_type, Variant::Type p_builtin_type, bool p_force = false);
+ void push_completion_call(Node *p_call);
+ void pop_completion_call();
+ void set_last_completion_call_arg(int p_argument);
+
+ GDScriptTokenizer::Token advance();
+ bool match(GDScriptTokenizer::Token::Type p_token_type);
+ bool check(GDScriptTokenizer::Token::Type p_token_type);
+ bool consume(GDScriptTokenizer::Token::Type p_token_type, const String &p_error_message);
+ bool is_at_end();
+ bool is_statement_end();
+ void end_statement(const String &p_context);
+ void synchronize();
+ void push_multiline(bool p_state);
+ void pop_multiline();
+
+ // Main blocks.
+ void parse_program();
+ ClassNode *parse_class();
+ void parse_class_name();
+ void parse_extends();
+ void parse_class_body();
+ template <class T>
+ void parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind);
+ SignalNode *parse_signal();
+ EnumNode *parse_enum();
+ ParameterNode *parse_parameter();
+ FunctionNode *parse_function();
+ SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr);
+ // Annotations
+ AnnotationNode *parse_annotation(uint32_t p_valid_targets);
+ bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false);
+ bool validate_annotation_arguments(AnnotationNode *p_annotation);
+ void clear_unused_annotations();
+ bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target);
+ bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target);
+ bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target);
+ template <PropertyHint t_hint, Variant::Type t_type>
+ bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
+ bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
+ template <MultiplayerAPI::RPCMode t_mode>
+ bool network_annotations(const AnnotationNode *p_annotation, Node *p_target);
+ // Statements.
+ Node *parse_statement();
+ VariableNode *parse_variable();
+ VariableNode *parse_variable(bool p_allow_property);
+ VariableNode *parse_property(VariableNode *p_variable, bool p_need_indent);
+ void parse_property_getter(VariableNode *p_variable);
+ void parse_property_setter(VariableNode *p_variable);
+ ConstantNode *parse_constant();
+ AssertNode *parse_assert();
+ BreakNode *parse_break();
+ ContinueNode *parse_continue();
+ ForNode *parse_for();
+ IfNode *parse_if(const String &p_token = "if");
+ MatchNode *parse_match();
+ MatchBranchNode *parse_match_branch();
+ PatternNode *parse_match_pattern(PatternNode *p_root_pattern = nullptr);
+ WhileNode *parse_while();
+ // Expressions.
+ ExpressionNode *parse_expression(bool p_can_assign, bool p_stop_on_assign = false);
+ ExpressionNode *parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign = false);
+ ExpressionNode *parse_literal(ExpressionNode *p_previous_operand, bool p_can_assign);
+ LiteralNode *parse_literal();
+ ExpressionNode *parse_self(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign);
+ IdentifierNode *parse_identifier();
+ ExpressionNode *parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_unary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_array(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_dictionary(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_call(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_grouping(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_await(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign);
+ TypeNode *parse_type(bool p_allow_void = false);
+
+public:
+ Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
+ ClassNode *get_tree() const { return head; }
+ bool is_tool() const { return _is_tool; }
+ static Variant::Type get_builtin_type(const StringName &p_type);
+ static GDScriptFunctions::Function get_builtin_function(const StringName &p_name);
+
+ CompletionContext get_completion_context() const { return completion_context; }
+ CompletionCall get_completion_call() const { return completion_call; }
+ void get_annotation_list(List<MethodInfo> *r_annotations) const;
+
+ const List<ParserError> &get_errors() const { return errors; }
+ const List<String> get_dependencies() const {
+ // TODO: Keep track of deps.
+ return List<String>();
+ }
+#ifdef DEBUG_ENABLED
+ const List<GDScriptWarning> &get_warnings() const { return warnings; }
+ const Set<int> &get_unsafe_lines() const { return unsafe_lines; }
+ int get_last_line_number() const { return current.end_line; }
+#endif
+
GDScriptParser();
~GDScriptParser();
+
+#ifdef DEBUG_ENABLED
+ class TreePrinter {
+ int indent_level = 0;
+ String indent;
+ StringBuilder printed;
+ bool pending_indent = false;
+
+ void increase_indent();
+ void decrease_indent();
+ void push_line(const String &p_line = String());
+ void push_text(const String &p_text);
+
+ void print_annotation(AnnotationNode *p_annotation);
+ void print_array(ArrayNode *p_array);
+ void print_assert(AssertNode *p_assert);
+ void print_assignment(AssignmentNode *p_assignment);
+ void print_await(AwaitNode *p_await);
+ void print_binary_op(BinaryOpNode *p_binary_op);
+ void print_call(CallNode *p_call);
+ void print_cast(CastNode *p_cast);
+ void print_class(ClassNode *p_class);
+ void print_constant(ConstantNode *p_constant);
+ void print_dictionary(DictionaryNode *p_dictionary);
+ void print_expression(ExpressionNode *p_expression);
+ void print_enum(EnumNode *p_enum);
+ void print_for(ForNode *p_for);
+ void print_function(FunctionNode *p_function);
+ void print_get_node(GetNodeNode *p_get_node);
+ void print_if(IfNode *p_if, bool p_is_elif = false);
+ void print_identifier(IdentifierNode *p_identifier);
+ void print_literal(LiteralNode *p_literal);
+ void print_match(MatchNode *p_match);
+ void print_match_branch(MatchBranchNode *p_match_branch);
+ void print_match_pattern(PatternNode *p_match_pattern);
+ void print_parameter(ParameterNode *p_parameter);
+ void print_preload(PreloadNode *p_preload);
+ void print_return(ReturnNode *p_return);
+ void print_self(SelfNode *p_self);
+ void print_signal(SignalNode *p_signal);
+ void print_statement(Node *p_statement);
+ void print_subscript(SubscriptNode *p_subscript);
+ void print_suite(SuiteNode *p_suite);
+ void print_type(TypeNode *p_type);
+ void print_ternary_op(TernaryOpNode *p_ternary_op);
+ void print_unary_op(UnaryOpNode *p_unary_op);
+ void print_variable(VariableNode *p_variable);
+ void print_while(WhileNode *p_while);
+
+ public:
+ void print_tree(const GDScriptParser &p_parser);
+ };
+#endif // DEBUG_ENABLED
+ static void cleanup();
};
#endif // GDSCRIPT_PARSER_H
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 2db42601c6..9a40aa50ac 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -30,1476 +30,1384 @@
#include "gdscript_tokenizer.h"
-#include "core/io/marshalls.h"
-#include "core/map.h"
-#include "core/print_string.h"
-#include "gdscript_functions.h"
-
-const char *GDScriptTokenizer::token_names[TK_MAX] = {
- "Empty",
- "Identifier",
- "Constant",
- "Self",
- "Built-In Type",
- "Built-In Func",
- "In",
- "'=='",
- "'!='",
- "'<'",
- "'<='",
- "'>'",
- "'>='",
- "'and'",
- "'or'",
- "'not'",
- "'+'",
- "'-'",
- "'*'",
- "'/'",
- "'%'",
- "'<<'",
- "'>>'",
- "'='",
- "'+='",
- "'-='",
- "'*='",
- "'/='",
- "'%='",
- "'<<='",
- "'>>='",
- "'&='",
- "'|='",
- "'^='",
- "'&'",
- "'|'",
- "'^'",
- "'~'",
- //"Plus Plus",
- //"Minus Minus",
- "if",
- "elif",
- "else",
- "for",
- "while",
- "break",
- "continue",
- "pass",
- "return",
- "match",
- "func",
- "class",
- "class_name",
- "extends",
- "is",
- "onready",
- "tool",
- "static",
- "export",
- "setget",
- "const",
- "var",
- "as",
- "void",
- "enum",
- "preload",
- "assert",
- "yield",
- "signal",
- "breakpoint",
- "remote",
- "master",
- "puppet",
- "remotesync",
- "mastersync",
- "puppetsync",
- "'['",
- "']'",
- "'{'",
- "'}'",
- "'('",
- "')'",
- "','",
- "';'",
- "'.'",
- "'?'",
- "':'",
- "'$'",
- "'->'",
- "'\\n'",
- "PI",
- "TAU",
- "_",
- "INF",
- "NAN",
- "Error",
- "EOF",
- "Cursor"
+#include "core/error_macros.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif
+
+static const char *token_names[] = {
+ "Empty", // EMPTY,
+ // Basic
+ "Annotation", // ANNOTATION
+ "Identifier", // IDENTIFIER,
+ "Literal", // LITERAL,
+ // Comparison
+ "<", // LESS,
+ "<=", // LESS_EQUAL,
+ ">", // GREATER,
+ ">=", // GREATER_EQUAL,
+ "==", // EQUAL_EQUAL,
+ "!=", // BANG_EQUAL,
+ // Logical
+ "and", // AND,
+ "or", // OR,
+ "not", // NOT,
+ "&&", // AMPERSAND_AMPERSAND,
+ "||", // PIPE_PIPE,
+ "!", // BANG,
+ // Bitwise
+ "&", // AMPERSAND,
+ "|", // PIPE,
+ "~", // TILDE,
+ "^", // CARET,
+ "<<", // LESS_LESS,
+ ">>", // GREATER_GREATER,
+ // Math
+ "+", // PLUS,
+ "-", // MINUS,
+ "*", // STAR,
+ "/", // SLASH,
+ "%", // PERCENT,
+ // Assignment
+ "=", // EQUAL,
+ "+=", // PLUS_EQUAL,
+ "-=", // MINUS_EQUAL,
+ "*=", // STAR_EQUAL,
+ "/=", // SLASH_EQUAL,
+ "%=", // PERCENT_EQUAL,
+ "<<=", // LESS_LESS_EQUAL,
+ ">>=", // GREATER_GREATER_EQUAL,
+ "&=", // AMPERSAND_EQUAL,
+ "|=", // PIPE_EQUAL,
+ "^=", // CARET_EQUAL,
+ // Control flow
+ "if", // IF,
+ "elif", // ELIF,
+ "else", // ELSE,
+ "for", // FOR,
+ "while", // WHILE,
+ "break", // BREAK,
+ "continue", // CONTINUE,
+ "pass", // PASS,
+ "return", // RETURN,
+ "match", // MATCH,
+ // Keywords
+ "as", // AS,
+ "assert", // ASSERT,
+ "await", // AWAIT,
+ "breakpoint", // BREAKPOINT,
+ "class", // CLASS,
+ "class_name", // CLASS_NAME,
+ "const", // CONST,
+ "enum", // ENUM,
+ "extends", // EXTENDS,
+ "func", // FUNC,
+ "in", // IN,
+ "is", // IS,
+ "namespace", // NAMESPACE
+ "preload", // PRELOAD,
+ "self", // SELF,
+ "signal", // SIGNAL,
+ "static", // STATIC,
+ "super", // SUPER,
+ "trait", // TRAIT,
+ "var", // VAR,
+ "void", // VOID,
+ "yield", // YIELD,
+ // Punctuation
+ "[", // BRACKET_OPEN,
+ "]", // BRACKET_CLOSE,
+ "{", // BRACE_OPEN,
+ "}", // BRACE_CLOSE,
+ "(", // PARENTHESIS_OPEN,
+ ")", // PARENTHESIS_CLOSE,
+ ",", // COMMA,
+ ";", // SEMICOLON,
+ ".", // PERIOD,
+ "..", // PERIOD_PERIOD,
+ ":", // COLON,
+ "$", // DOLLAR,
+ "->", // FORWARD_ARROW,
+ "_", // UNDERSCORE,
+ // Whitespace
+ "Newline", // NEWLINE,
+ "Indent", // INDENT,
+ "Dedent", // DEDENT,
+ // Constants
+ "PI", // CONST_PI,
+ "TAU", // CONST_TAU,
+ "INF", // CONST_INF,
+ "NaN", // CONST_NAN,
+ // Error message improvement
+ "VCS conflict marker", // VCS_CONFLICT_MARKER,
+ "`", // BACKTICK,
+ "?", // QUESTION_MARK,
+ // Special
+ "Error", // ERROR,
+ "End of file", // EOF,
};
-struct _bit {
- Variant::Type type;
- const char *text;
-};
-//built in types
-
-static const _bit _type_list[] = {
- //types
- { Variant::BOOL, "bool" },
- { Variant::INT, "int" },
- { Variant::FLOAT, "float" },
- { Variant::STRING, "String" },
- { Variant::VECTOR2, "Vector2" },
- { Variant::VECTOR2I, "Vector2i" },
- { Variant::RECT2, "Rect2" },
- { Variant::RECT2I, "Rect2i" },
- { Variant::TRANSFORM2D, "Transform2D" },
- { Variant::VECTOR3, "Vector3" },
- { Variant::VECTOR3I, "Vector3i" },
- { Variant::AABB, "AABB" },
- { Variant::PLANE, "Plane" },
- { Variant::QUAT, "Quat" },
- { Variant::BASIS, "Basis" },
- { Variant::TRANSFORM, "Transform" },
- { Variant::COLOR, "Color" },
- { Variant::_RID, "RID" },
- { Variant::OBJECT, "Object" },
- { Variant::STRING_NAME, "StringName" },
- { Variant::NODE_PATH, "NodePath" },
- { Variant::DICTIONARY, "Dictionary" },
- { Variant::CALLABLE, "Callable" },
- { Variant::SIGNAL, "Signal" },
- { Variant::ARRAY, "Array" },
- { Variant::PACKED_BYTE_ARRAY, "PackedByteArray" },
- { Variant::PACKED_INT32_ARRAY, "PackedInt32Array" },
- { Variant::PACKED_INT64_ARRAY, "PackedInt64Array" },
- { Variant::PACKED_FLOAT32_ARRAY, "PackedFloat32Array" },
- { Variant::PACKED_FLOAT64_ARRAY, "PackedFloat64Array" },
- { Variant::PACKED_STRING_ARRAY, "PackedStringArray" },
- { Variant::PACKED_VECTOR2_ARRAY, "PackedVector2Array" },
- { Variant::PACKED_VECTOR3_ARRAY, "PackedVector3Array" },
- { Variant::PACKED_COLOR_ARRAY, "PackedColorArray" },
- { Variant::VARIANT_MAX, nullptr },
-};
-
-struct _kws {
- GDScriptTokenizer::Token token;
- const char *text;
-};
-
-static const _kws _keyword_list[] = {
- //ops
- { GDScriptTokenizer::TK_OP_IN, "in" },
- { GDScriptTokenizer::TK_OP_NOT, "not" },
- { GDScriptTokenizer::TK_OP_OR, "or" },
- { GDScriptTokenizer::TK_OP_AND, "and" },
- //func
- { GDScriptTokenizer::TK_PR_FUNCTION, "func" },
- { GDScriptTokenizer::TK_PR_CLASS, "class" },
- { GDScriptTokenizer::TK_PR_CLASS_NAME, "class_name" },
- { GDScriptTokenizer::TK_PR_EXTENDS, "extends" },
- { GDScriptTokenizer::TK_PR_IS, "is" },
- { GDScriptTokenizer::TK_PR_ONREADY, "onready" },
- { GDScriptTokenizer::TK_PR_TOOL, "tool" },
- { GDScriptTokenizer::TK_PR_STATIC, "static" },
- { GDScriptTokenizer::TK_PR_EXPORT, "export" },
- { GDScriptTokenizer::TK_PR_SETGET, "setget" },
- { GDScriptTokenizer::TK_PR_VAR, "var" },
- { GDScriptTokenizer::TK_PR_AS, "as" },
- { GDScriptTokenizer::TK_PR_VOID, "void" },
- { GDScriptTokenizer::TK_PR_PRELOAD, "preload" },
- { GDScriptTokenizer::TK_PR_ASSERT, "assert" },
- { GDScriptTokenizer::TK_PR_YIELD, "yield" },
- { GDScriptTokenizer::TK_PR_SIGNAL, "signal" },
- { GDScriptTokenizer::TK_PR_BREAKPOINT, "breakpoint" },
- { GDScriptTokenizer::TK_PR_REMOTE, "remote" },
- { GDScriptTokenizer::TK_PR_MASTER, "master" },
- { GDScriptTokenizer::TK_PR_PUPPET, "puppet" },
- { GDScriptTokenizer::TK_PR_REMOTESYNC, "remotesync" },
- { GDScriptTokenizer::TK_PR_MASTERSYNC, "mastersync" },
- { GDScriptTokenizer::TK_PR_PUPPETSYNC, "puppetsync" },
- { GDScriptTokenizer::TK_PR_CONST, "const" },
- { GDScriptTokenizer::TK_PR_ENUM, "enum" },
- //controlflow
- { GDScriptTokenizer::TK_CF_IF, "if" },
- { GDScriptTokenizer::TK_CF_ELIF, "elif" },
- { GDScriptTokenizer::TK_CF_ELSE, "else" },
- { GDScriptTokenizer::TK_CF_FOR, "for" },
- { GDScriptTokenizer::TK_CF_WHILE, "while" },
- { GDScriptTokenizer::TK_CF_BREAK, "break" },
- { GDScriptTokenizer::TK_CF_CONTINUE, "continue" },
- { GDScriptTokenizer::TK_CF_RETURN, "return" },
- { GDScriptTokenizer::TK_CF_MATCH, "match" },
- { GDScriptTokenizer::TK_CF_PASS, "pass" },
- { GDScriptTokenizer::TK_SELF, "self" },
- { GDScriptTokenizer::TK_CONST_PI, "PI" },
- { GDScriptTokenizer::TK_CONST_TAU, "TAU" },
- { GDScriptTokenizer::TK_WILDCARD, "_" },
- { GDScriptTokenizer::TK_CONST_INF, "INF" },
- { GDScriptTokenizer::TK_CONST_NAN, "NAN" },
- { GDScriptTokenizer::TK_ERROR, nullptr }
-};
+// Avoid desync.
+static_assert(sizeof(token_names) / sizeof(token_names[0]) == GDScriptTokenizer::Token::TK_MAX, "Amount of token names don't match the amount of token types.");
-const char *GDScriptTokenizer::get_token_name(Token p_token) {
- ERR_FAIL_INDEX_V(p_token, TK_MAX, "<error>");
- return token_names[p_token];
+const char *GDScriptTokenizer::Token::get_name() const {
+ ERR_FAIL_INDEX_V_MSG(type, TK_MAX, "<error>", "Using token type out of the enum.");
+ return token_names[type];
}
-bool GDScriptTokenizer::is_token_literal(int p_offset, bool variable_safe) const {
- switch (get_token(p_offset)) {
- // Can always be literal:
- case TK_IDENTIFIER:
-
- case TK_PR_ONREADY:
- case TK_PR_TOOL:
- case TK_PR_STATIC:
- case TK_PR_EXPORT:
- case TK_PR_SETGET:
- case TK_PR_SIGNAL:
- case TK_PR_REMOTE:
- case TK_PR_MASTER:
- case TK_PR_PUPPET:
- case TK_PR_REMOTESYNC:
- case TK_PR_MASTERSYNC:
- case TK_PR_PUPPETSYNC:
+bool GDScriptTokenizer::Token::is_identifier() const {
+ // Note: Most keywords should not be recognized as identifiers.
+ // These are only exceptions for stuff that already is on the engine's API.
+ switch (type) {
+ case IDENTIFIER:
+ case MATCH: // Used in String.match().
return true;
-
- // Literal for non-variables only:
- case TK_BUILT_IN_TYPE:
- case TK_BUILT_IN_FUNC:
-
- case TK_OP_IN:
- //case TK_OP_NOT:
- //case TK_OP_OR:
- //case TK_OP_AND:
-
- case TK_PR_CLASS:
- case TK_PR_CONST:
- case TK_PR_ENUM:
- case TK_PR_PRELOAD:
- case TK_PR_FUNCTION:
- case TK_PR_EXTENDS:
- case TK_PR_ASSERT:
- case TK_PR_YIELD:
- case TK_PR_VAR:
-
- case TK_CF_IF:
- case TK_CF_ELIF:
- case TK_CF_ELSE:
- case TK_CF_FOR:
- case TK_CF_WHILE:
- case TK_CF_BREAK:
- case TK_CF_CONTINUE:
- case TK_CF_RETURN:
- case TK_CF_MATCH:
- case TK_CF_PASS:
- case TK_SELF:
- case TK_CONST_PI:
- case TK_CONST_TAU:
- case TK_WILDCARD:
- case TK_CONST_INF:
- case TK_CONST_NAN:
- case TK_ERROR:
- return !variable_safe;
-
- case TK_CONSTANT: {
- switch (get_token_constant(p_offset).get_type()) {
- case Variant::NIL:
- case Variant::BOOL:
- return true;
- default:
- return false;
- }
- }
default:
return false;
}
}
-StringName GDScriptTokenizer::get_token_literal(int p_offset) const {
- Token token = get_token(p_offset);
- switch (token) {
- case TK_IDENTIFIER:
- return get_token_identifier(p_offset);
- case TK_BUILT_IN_TYPE: {
- Variant::Type type = get_token_type(p_offset);
- int idx = 0;
-
- while (_type_list[idx].text) {
- if (type == _type_list[idx].type) {
- return _type_list[idx].text;
- }
- idx++;
- }
- } break; // Shouldn't get here, stuff happens
- case TK_BUILT_IN_FUNC:
- return GDScriptFunctions::get_func_name(get_token_built_in_func(p_offset));
- case TK_CONSTANT: {
- const Variant value = get_token_constant(p_offset);
-
- switch (value.get_type()) {
- case Variant::NIL:
- return "null";
- case Variant::BOOL:
- return value ? "true" : "false";
- default: {
- }
- }
- } break;
- case TK_OP_AND:
- case TK_OP_OR:
- break; // Don't get into default, since they can be non-literal
- default: {
- int idx = 0;
-
- while (_keyword_list[idx].text) {
- if (token == _keyword_list[idx].token) {
- return _keyword_list[idx].text;
- }
- idx++;
- }
- }
+bool GDScriptTokenizer::Token::is_node_name() const {
+ // This is meant to allow keywords with the $ notation, but not as general identifiers.
+ switch (type) {
+ case IDENTIFIER:
+ case AND:
+ case AS:
+ case ASSERT:
+ case AWAIT:
+ case BREAK:
+ case BREAKPOINT:
+ case CLASS_NAME:
+ case CLASS:
+ case CONST:
+ case CONTINUE:
+ case ELIF:
+ case ELSE:
+ case ENUM:
+ case EXTENDS:
+ case FOR:
+ case FUNC:
+ case IF:
+ case IN:
+ case IS:
+ case MATCH:
+ case NAMESPACE:
+ case NOT:
+ case OR:
+ case PASS:
+ case PRELOAD:
+ case RETURN:
+ case SELF:
+ case SIGNAL:
+ case STATIC:
+ case SUPER:
+ case TRAIT:
+ case UNDERSCORE:
+ case VAR:
+ case VOID:
+ case WHILE:
+ case YIELD:
+ return true;
+ default:
+ return false;
}
- ERR_FAIL_V_MSG("", "Failed to get token literal.");
}
-static bool _is_text_char(CharType c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+String GDScriptTokenizer::get_token_name(Token::Type p_token_type) {
+ ERR_FAIL_INDEX_V_MSG(p_token_type, Token::TK_MAX, "<error>", "Using token type out of the enum.");
+ return token_names[p_token_type];
}
-static bool _is_number(CharType c) {
- return (c >= '0' && c <= '9');
+void GDScriptTokenizer::set_source_code(const String &p_source_code) {
+ source = p_source_code;
+ if (source.empty()) {
+ _source = U"";
+ } else {
+ _source = source.ptr();
+ }
+ _current = _source;
+ line = 1;
+ column = 1;
+ length = p_source_code.length();
+ position = 0;
}
-static bool _is_hex(CharType c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+void GDScriptTokenizer::set_cursor_position(int p_line, int p_column) {
+ cursor_line = p_line;
+ cursor_column = p_column;
}
-static bool _is_bin(CharType c) {
- return (c == '0' || c == '1');
+void GDScriptTokenizer::set_multiline_mode(bool p_state) {
+ multiline_mode = p_state;
}
-void GDScriptTokenizerText::_make_token(Token p_type) {
- TokenData &tk = tk_rb[tk_rb_pos];
-
- tk.type = p_type;
- tk.line = line;
- tk.col = column;
-
- tk_rb_pos = (tk_rb_pos + 1) % TK_RB_SIZE;
+int GDScriptTokenizer::get_cursor_line() const {
+ return cursor_line;
}
-void GDScriptTokenizerText::_make_identifier(const StringName &p_identifier) {
- TokenData &tk = tk_rb[tk_rb_pos];
-
- tk.type = TK_IDENTIFIER;
- tk.identifier = p_identifier;
- tk.line = line;
- tk.col = column;
-
- tk_rb_pos = (tk_rb_pos + 1) % TK_RB_SIZE;
+int GDScriptTokenizer::get_cursor_column() const {
+ return cursor_column;
}
-void GDScriptTokenizerText::_make_built_in_func(GDScriptFunctions::Function p_func) {
- TokenData &tk = tk_rb[tk_rb_pos];
-
- tk.type = TK_BUILT_IN_FUNC;
- tk.func = p_func;
- tk.line = line;
- tk.col = column;
-
- tk_rb_pos = (tk_rb_pos + 1) % TK_RB_SIZE;
+bool GDScriptTokenizer::is_past_cursor() const {
+ if (line < cursor_line) {
+ return false;
+ }
+ if (line > cursor_line) {
+ return true;
+ }
+ if (column < cursor_column) {
+ return false;
+ }
+ return true;
}
-void GDScriptTokenizerText::_make_constant(const Variant &p_constant) {
- TokenData &tk = tk_rb[tk_rb_pos];
-
- tk.type = TK_CONSTANT;
- tk.constant = p_constant;
- tk.line = line;
- tk.col = column;
-
- tk_rb_pos = (tk_rb_pos + 1) % TK_RB_SIZE;
+char32_t GDScriptTokenizer::_advance() {
+ if (unlikely(_is_at_end())) {
+ return '\0';
+ }
+ _current++;
+ column++;
+ position++;
+ if (column > rightmost_column) {
+ rightmost_column = column;
+ }
+ if (unlikely(_is_at_end())) {
+ // Add extra newline even if it's not there, to satisfy the parser.
+ newline(true);
+ // Also add needed unindent.
+ check_indent();
+ }
+ return _peek(-1);
}
-void GDScriptTokenizerText::_make_type(const Variant::Type &p_type) {
- TokenData &tk = tk_rb[tk_rb_pos];
-
- tk.type = TK_BUILT_IN_TYPE;
- tk.vtype = p_type;
- tk.line = line;
- tk.col = column;
-
- tk_rb_pos = (tk_rb_pos + 1) % TK_RB_SIZE;
+void GDScriptTokenizer::push_paren(char32_t p_char) {
+ paren_stack.push_back(p_char);
}
-void GDScriptTokenizerText::_make_error(const String &p_error) {
- error_flag = true;
- last_error = p_error;
+bool GDScriptTokenizer::pop_paren(char32_t p_expected) {
+ if (paren_stack.empty()) {
+ return false;
+ }
+ char32_t actual = paren_stack.back()->get();
+ paren_stack.pop_back();
- TokenData &tk = tk_rb[tk_rb_pos];
- tk.type = TK_ERROR;
- tk.constant = p_error;
- tk.line = line;
- tk.col = column;
- tk_rb_pos = (tk_rb_pos + 1) % TK_RB_SIZE;
+ return actual == p_expected;
}
-void GDScriptTokenizerText::_make_newline(int p_indentation, int p_tabs) {
- TokenData &tk = tk_rb[tk_rb_pos];
- tk.type = TK_NEWLINE;
- tk.constant = Vector2(p_indentation, p_tabs);
- tk.line = line;
- tk.col = column;
- tk_rb_pos = (tk_rb_pos + 1) % TK_RB_SIZE;
+GDScriptTokenizer::Token GDScriptTokenizer::pop_error() {
+ Token error = error_stack.back()->get();
+ error_stack.pop_back();
+ return error;
}
-void GDScriptTokenizerText::_advance() {
- if (error_flag) {
- //parser broke
- _make_error(last_error);
- return;
- }
-
- if (code_pos >= len) {
- _make_token(TK_EOF);
- return;
- }
-#define GETCHAR(m_ofs) ((m_ofs + code_pos) >= len ? 0 : _code[m_ofs + code_pos])
-#define INCPOS(m_amount) \
- { \
- code_pos += m_amount; \
- column += m_amount; \
- }
- while (true) {
- bool is_string_name = false;
- StringMode string_mode = STRING_DOUBLE_QUOTE;
-
- switch (GETCHAR(0)) {
- case 0:
- _make_token(TK_EOF);
- break;
- case '\\':
- INCPOS(1);
- if (GETCHAR(0) == '\r') {
- INCPOS(1);
- }
-
- if (GETCHAR(0) != '\n') {
- _make_error("Expected newline after '\\'.");
- return;
- }
-
- INCPOS(1);
- line++;
-
- while (GETCHAR(0) == ' ' || GETCHAR(0) == '\t') {
- INCPOS(1);
- }
-
- continue;
- case '\t':
- case '\r':
- case ' ':
- INCPOS(1);
- continue;
- case '#': { // line comment skip
-#ifdef DEBUG_ENABLED
- String comment;
-#endif // DEBUG_ENABLED
- while (GETCHAR(0) != '\n') {
-#ifdef DEBUG_ENABLED
- comment += GETCHAR(0);
-#endif // DEBUG_ENABLED
- code_pos++;
- if (GETCHAR(0) == 0) { //end of file
- //_make_error("Unterminated Comment");
- _make_token(TK_EOF);
- return;
- }
- }
-#ifdef DEBUG_ENABLED
- String comment_content = comment.trim_prefix("#").trim_prefix(" ");
- if (comment_content.begins_with("warning-ignore:")) {
- String code = comment_content.get_slice(":", 1);
- warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower()));
- } else if (comment_content.begins_with("warning-ignore-all:")) {
- String code = comment_content.get_slice(":", 1);
- warning_global_skips.insert(code.strip_edges().to_lower());
- } else if (comment_content.strip_edges() == "warnings-disable") {
- ignore_warnings = true;
- }
-#endif // DEBUG_ENABLED
- [[fallthrough]];
- }
- case '\n': {
- line++;
- INCPOS(1);
- bool used_spaces = false;
- int tabs = 0;
- column = 1;
- int i = 0;
- while (true) {
- if (GETCHAR(i) == ' ') {
- i++;
- used_spaces = true;
- } else if (GETCHAR(i) == '\t') {
- if (used_spaces) {
- _make_error("Spaces used before tabs on a line");
- return;
- }
- i++;
- tabs++;
- } else {
- break; // not indentation anymore
- }
- }
-
- _make_newline(i, tabs);
- return;
- }
- case '/': {
- switch (GETCHAR(1)) {
- case '=': { // diveq
-
- _make_token(TK_OP_ASSIGN_DIV);
- INCPOS(1);
-
- } break;
- default:
- _make_token(TK_OP_DIV);
- }
- } break;
- case '=': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_EQUAL);
- INCPOS(1);
-
- } else {
- _make_token(TK_OP_ASSIGN);
- }
-
- } break;
- case '<': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_LESS_EQUAL);
- INCPOS(1);
- } else if (GETCHAR(1) == '<') {
- if (GETCHAR(2) == '=') {
- _make_token(TK_OP_ASSIGN_SHIFT_LEFT);
- INCPOS(1);
- } else {
- _make_token(TK_OP_SHIFT_LEFT);
- }
- INCPOS(1);
- } else {
- _make_token(TK_OP_LESS);
- }
-
- } break;
- case '>': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_GREATER_EQUAL);
- INCPOS(1);
- } else if (GETCHAR(1) == '>') {
- if (GETCHAR(2) == '=') {
- _make_token(TK_OP_ASSIGN_SHIFT_RIGHT);
- INCPOS(1);
-
- } else {
- _make_token(TK_OP_SHIFT_RIGHT);
- }
- INCPOS(1);
- } else {
- _make_token(TK_OP_GREATER);
- }
+static bool _is_alphanumeric(char32_t c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
- } break;
- case '!': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_NOT_EQUAL);
- INCPOS(1);
- } else {
- _make_token(TK_OP_NOT);
- }
+static bool _is_digit(char32_t c) {
+ return (c >= '0' && c <= '9');
+}
- } break;
- //case '"' //string - no strings in shader
- //case '\'' //string - no strings in shader
- case '{':
- _make_token(TK_CURLY_BRACKET_OPEN);
- break;
- case '}':
- _make_token(TK_CURLY_BRACKET_CLOSE);
- break;
- case '[':
- _make_token(TK_BRACKET_OPEN);
- break;
- case ']':
- _make_token(TK_BRACKET_CLOSE);
- break;
- case '(':
- _make_token(TK_PARENTHESIS_OPEN);
- break;
- case ')':
- _make_token(TK_PARENTHESIS_CLOSE);
- break;
- case ',':
- _make_token(TK_COMMA);
- break;
- case ';':
- _make_token(TK_SEMICOLON);
- break;
- case '?':
- _make_token(TK_QUESTION_MARK);
- break;
- case ':':
- _make_token(TK_COLON); //for methods maybe but now useless.
- break;
- case '$':
- _make_token(TK_DOLLAR); //for the get_node() shortener
- break;
- case '^': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_ASSIGN_BIT_XOR);
- INCPOS(1);
- } else {
- _make_token(TK_OP_BIT_XOR);
- }
+static bool _is_hex_digit(char32_t c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
- } break;
- case '~':
- _make_token(TK_OP_BIT_INVERT);
- break;
- case '&': {
- if (GETCHAR(1) == '&') {
- _make_token(TK_OP_AND);
- INCPOS(1);
- } else if (GETCHAR(1) == '=') {
- _make_token(TK_OP_ASSIGN_BIT_AND);
- INCPOS(1);
- } else {
- _make_token(TK_OP_BIT_AND);
- }
- } break;
- case '|': {
- if (GETCHAR(1) == '|') {
- _make_token(TK_OP_OR);
- INCPOS(1);
- } else if (GETCHAR(1) == '=') {
- _make_token(TK_OP_ASSIGN_BIT_OR);
- INCPOS(1);
- } else {
- _make_token(TK_OP_BIT_OR);
- }
- } break;
- case '*': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_ASSIGN_MUL);
- INCPOS(1);
- } else {
- _make_token(TK_OP_MUL);
- }
- } break;
- case '+': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_ASSIGN_ADD);
- INCPOS(1);
- /*
- } else if (GETCHAR(1)=='+') {
- _make_token(TK_OP_PLUS_PLUS);
- INCPOS(1);
- */
- } else {
- _make_token(TK_OP_ADD);
- }
+static bool _is_binary_digit(char32_t c) {
+ return (c == '0' || c == '1');
+}
- } break;
- case '-': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_ASSIGN_SUB);
- INCPOS(1);
- } else if (GETCHAR(1) == '>') {
- _make_token(TK_FORWARD_ARROW);
- INCPOS(1);
+GDScriptTokenizer::Token GDScriptTokenizer::make_token(Token::Type p_type) {
+ Token token(p_type);
+ token.start_line = start_line;
+ token.end_line = line;
+ token.start_column = start_column;
+ token.end_column = column;
+ token.leftmost_column = leftmost_column;
+ token.rightmost_column = rightmost_column;
+ token.source = String(_start, _current - _start);
+
+ if (p_type != Token::ERROR && cursor_line > -1) {
+ // Also count whitespace after token.
+ int offset = 0;
+ while (_peek(offset) == ' ' || _peek(offset) == '\t') {
+ offset++;
+ }
+ int last_column = column + offset;
+ // Check cursor position in token.
+ if (start_line == line) {
+ // Single line token.
+ if (cursor_line == start_line && cursor_column >= start_column && cursor_column <= last_column) {
+ token.cursor_position = cursor_column - start_column;
+ if (cursor_column == start_column) {
+ token.cursor_place = CURSOR_BEGINNING;
+ } else if (cursor_column < column) {
+ token.cursor_place = CURSOR_MIDDLE;
} else {
- _make_token(TK_OP_SUB);
+ token.cursor_place = CURSOR_END;
}
- } break;
- case '%': {
- if (GETCHAR(1) == '=') {
- _make_token(TK_OP_ASSIGN_MOD);
- INCPOS(1);
+ }
+ } else {
+ // Multi line token.
+ if (cursor_line == start_line && cursor_column >= start_column) {
+ // Is in first line.
+ token.cursor_position = cursor_column - start_column;
+ if (cursor_column == start_column) {
+ token.cursor_place = CURSOR_BEGINNING;
} else {
- _make_token(TK_OP_MOD);
- }
- } break;
- case '@':
- if (CharType(GETCHAR(1)) != '"' && CharType(GETCHAR(1)) != '\'') {
- _make_error("Unexpected '@'");
- return;
- }
- INCPOS(1);
- is_string_name = true;
- [[fallthrough]];
- case '\'':
- case '"': {
- if (GETCHAR(0) == '\'') {
- string_mode = STRING_SINGLE_QUOTE;
- }
-
- int i = 1;
- if (string_mode == STRING_DOUBLE_QUOTE && GETCHAR(i) == '"' && GETCHAR(i + 1) == '"') {
- i += 2;
- string_mode = STRING_MULTILINE;
+ token.cursor_place = CURSOR_MIDDLE;
}
-
- String str;
- while (true) {
- if (CharType(GETCHAR(i)) == 0) {
- _make_error("Unterminated String");
- return;
- } else if (string_mode == STRING_DOUBLE_QUOTE && CharType(GETCHAR(i)) == '"') {
- break;
- } else if (string_mode == STRING_SINGLE_QUOTE && CharType(GETCHAR(i)) == '\'') {
- break;
- } else if (string_mode == STRING_MULTILINE && CharType(GETCHAR(i)) == '\"' && CharType(GETCHAR(i + 1)) == '\"' && CharType(GETCHAR(i + 2)) == '\"') {
- i += 2;
- break;
- } else if (string_mode != STRING_MULTILINE && CharType(GETCHAR(i)) == '\n') {
- _make_error("Unexpected EOL at String.");
- return;
- } else if (CharType(GETCHAR(i)) == 0xFFFF) {
- //string ends here, next will be TK
- i--;
- break;
- } else if (CharType(GETCHAR(i)) == '\\') {
- //escaped characters...
- i++;
- CharType next = GETCHAR(i);
- if (next == 0) {
- _make_error("Unterminated String");
- return;
- }
- CharType res = 0;
-
- switch (next) {
- case 'a':
- res = '\a';
- break;
- case 'b':
- res = '\b';
- break;
- case 't':
- res = '\t';
- break;
- case 'n':
- res = '\n';
- break;
- case 'v':
- res = '\v';
- break;
- case 'f':
- res = '\f';
- break;
- case 'r':
- res = '\r';
- break;
- case '\'':
- res = '\'';
- break;
- case '\"':
- res = '\"';
- break;
- case '\\':
- res = '\\';
- break;
-
- case 'u': {
- // hex number
- i += 1;
- for (int j = 0; j < 4; j++) {
- CharType c = GETCHAR(i + j);
- if (c == 0) {
- _make_error("Unterminated String");
- return;
- }
-
- CharType v = 0;
- if (c >= '0' && c <= '9') {
- v = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- v = c - 'a';
- v += 10;
- } else if (c >= 'A' && c <= 'F') {
- v = c - 'A';
- v += 10;
- } else {
- _make_error("Malformed hex constant in string");
- return;
- }
-
- res <<= 4;
- res |= v;
- }
- i += 3;
-
- } break;
- case '\n': {
- line++;
- column = 1;
- } break;
- default: {
- _make_error("Invalid escape sequence");
- return;
- } break;
- }
-
- if (next != '\n') {
- str += res;
- }
-
- } else {
- if (CharType(GETCHAR(i)) == '\n') {
- line++;
- column = 1;
- }
-
- str += CharType(GETCHAR(i));
- }
- i++;
- }
- INCPOS(i);
-
- if (is_string_name) {
- _make_constant(StringName(str));
+ } else if (cursor_line == line && cursor_column <= last_column) {
+ // Is in last line.
+ token.cursor_position = cursor_column - start_column;
+ if (cursor_column < column) {
+ token.cursor_place = CURSOR_MIDDLE;
} else {
- _make_constant(str);
- }
-
- } break;
- case 0xFFFF: {
- _make_token(TK_CURSOR);
- } break;
- default: {
- if (_is_number(GETCHAR(0)) || (GETCHAR(0) == '.' && _is_number(GETCHAR(1)))) {
- // parse number
- bool period_found = false;
- bool exponent_found = false;
- bool hexa_found = false;
- bool bin_found = false;
- bool sign_found = false;
-
- String str;
- int i = 0;
-
- while (true) {
- if (GETCHAR(i) == '.') {
- if (period_found || exponent_found) {
- _make_error("Invalid numeric constant at '.'");
- return;
- } else if (bin_found) {
- _make_error("Invalid binary constant at '.'");
- return;
- } else if (hexa_found) {
- _make_error("Invalid hexadecimal constant at '.'");
- return;
- }
- period_found = true;
- } else if (GETCHAR(i) == 'x') {
- if (hexa_found || bin_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) {
- _make_error("Invalid numeric constant at 'x'");
- return;
- }
- hexa_found = true;
- } else if (hexa_found && _is_hex(GETCHAR(i))) {
- } else if (!hexa_found && GETCHAR(i) == 'b') {
- if (bin_found || str.length() != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-'))) {
- _make_error("Invalid numeric constant at 'b'");
- return;
- }
- bin_found = true;
- } else if (!hexa_found && GETCHAR(i) == 'e') {
- if (exponent_found || bin_found) {
- _make_error("Invalid numeric constant at 'e'");
- return;
- }
- exponent_found = true;
- } else if (_is_number(GETCHAR(i))) {
- //all ok
-
- } else if (bin_found && _is_bin(GETCHAR(i))) {
- } else if ((GETCHAR(i) == '-' || GETCHAR(i) == '+') && exponent_found) {
- if (sign_found) {
- _make_error("Invalid numeric constant at '-'");
- return;
- }
- sign_found = true;
- } else if (GETCHAR(i) == '_') {
- i++;
- continue; // Included for readability, shouldn't be a part of the string
- } else {
- break;
- }
-
- str += CharType(GETCHAR(i));
- i++;
- }
-
- if (!(_is_number(str[str.length() - 1]) || (hexa_found && _is_hex(str[str.length() - 1])))) {
- _make_error("Invalid numeric constant: " + str);
- return;
- }
-
- INCPOS(i);
- if (hexa_found) {
- int64_t val = str.hex_to_int64();
- _make_constant(val);
- } else if (bin_found) {
- int64_t val = str.bin_to_int64();
- _make_constant(val);
- } else if (period_found || exponent_found) {
- double val = str.to_double();
- _make_constant(val);
- } else {
- int64_t val = str.to_int64();
- _make_constant(val);
- }
-
- return;
+ token.cursor_place = CURSOR_END;
}
+ } else if (cursor_line > start_line && cursor_line < line) {
+ // Is in middle line.
+ token.cursor_position = CURSOR_MIDDLE;
+ }
+ }
+ }
- if (GETCHAR(0) == '.') {
- //parse period
- _make_token(TK_PERIOD);
- break;
- }
-
- if (_is_text_char(GETCHAR(0))) {
- // parse identifier
- String str;
- str += CharType(GETCHAR(0));
-
- int i = 1;
- while (_is_text_char(GETCHAR(i))) {
- str += CharType(GETCHAR(i));
- i++;
- }
-
- bool identifier = false;
-
- if (str == "null") {
- _make_constant(Variant());
-
- } else if (str == "true") {
- _make_constant(true);
-
- } else if (str == "false") {
- _make_constant(false);
- } else {
- bool found = false;
-
- {
- int idx = 0;
-
- while (_type_list[idx].text) {
- if (str == _type_list[idx].text) {
- _make_type(_type_list[idx].type);
- found = true;
- break;
- }
- idx++;
- }
- }
+ return token;
+}
- if (!found) {
- //built in func?
+GDScriptTokenizer::Token GDScriptTokenizer::make_literal(const Variant &p_literal) {
+ Token token = make_token(Token::LITERAL);
+ token.literal = p_literal;
+ return token;
+}
- for (int j = 0; j < GDScriptFunctions::FUNC_MAX; j++) {
- if (str == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(j))) {
- _make_built_in_func(GDScriptFunctions::Function(j));
- found = true;
- break;
- }
- }
- }
+GDScriptTokenizer::Token GDScriptTokenizer::make_identifier(const StringName &p_identifier) {
+ Token identifier = make_token(Token::IDENTIFIER);
+ identifier.literal = p_identifier;
+ return identifier;
+}
- if (!found) {
- //keyword
+GDScriptTokenizer::Token GDScriptTokenizer::make_error(const String &p_message) {
+ Token error = make_token(Token::ERROR);
+ error.literal = p_message;
- int idx = 0;
- found = false;
+ return error;
+}
- while (_keyword_list[idx].text) {
- if (str == _keyword_list[idx].text) {
- _make_token(_keyword_list[idx].token);
- found = true;
- break;
- }
- idx++;
- }
- }
+void GDScriptTokenizer::push_error(const String &p_message) {
+ Token error = make_error(p_message);
+ error_stack.push_back(error);
+}
- if (!found) {
- identifier = true;
- }
- }
+void GDScriptTokenizer::push_error(const Token &p_error) {
+ error_stack.push_back(p_error);
+}
- if (identifier) {
- _make_identifier(str);
- }
- INCPOS(str.length());
- return;
- }
+GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) {
+ if (paren_stack.empty()) {
+ return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren));
+ }
+ Token error = make_error(vformat("Closing \"%c\" doesn't match the opening \"%c\".", p_paren, paren_stack.back()->get()));
+ paren_stack.pop_back(); // Remove opening one anyway.
+ return error;
+}
- _make_error("Unknown character");
- return;
+GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, Token::Type p_double_type) {
+ const char32_t *next = _current + 1;
+ int chars = 2; // Two already matched.
- } break;
+ // Test before consuming characters, since we don't want to consume more than needed.
+ while (*next == p_test) {
+ chars++;
+ next++;
+ }
+ if (chars >= 7) {
+ // It is a VCS conflict marker.
+ while (chars > 1) {
+ // Consume all characters (first was already consumed by scan()).
+ _advance();
+ chars--;
}
-
- INCPOS(1);
- break;
+ return make_token(Token::VCS_CONFLICT_MARKER);
+ } else {
+ // It is only a regular double character token, so we consume the second character.
+ _advance();
+ return make_token(p_double_type);
}
}
-void GDScriptTokenizerText::set_code(const String &p_code) {
- code = p_code;
- len = p_code.length();
- if (len) {
- _code = &code[0];
- } else {
- _code = nullptr;
+GDScriptTokenizer::Token GDScriptTokenizer::annotation() {
+ if (!_is_alphanumeric(_peek())) {
+ push_error("Expected annotation identifier after \"@\".");
}
- code_pos = 0;
- line = 1; //it is stand-ar-ized that lines begin in 1 in code..
- column = 1; //the same holds for columns
- tk_rb_pos = 0;
- error_flag = false;
-#ifdef DEBUG_ENABLED
- ignore_warnings = false;
-#endif // DEBUG_ENABLED
- last_error = "";
- for (int i = 0; i < MAX_LOOKAHEAD + 1; i++) {
+ while (_is_alphanumeric(_peek())) {
+ // Consume all identifier characters.
_advance();
}
+ Token annotation = make_token(Token::ANNOTATION);
+ annotation.literal = StringName(annotation.source);
+ return annotation;
}
-GDScriptTokenizerText::Token GDScriptTokenizerText::get_token(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, TK_ERROR);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, TK_ERROR);
-
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- return tk_rb[ofs].type;
-}
+GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
+#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
+ KEYWORD_GROUP('a') \
+ KEYWORD("as", Token::AS) \
+ KEYWORD("and", Token::AND) \
+ KEYWORD("assert", Token::ASSERT) \
+ KEYWORD("await", Token::AWAIT) \
+ KEYWORD_GROUP('b') \
+ KEYWORD("break", Token::BREAK) \
+ KEYWORD("breakpoint", Token::BREAKPOINT) \
+ KEYWORD_GROUP('c') \
+ KEYWORD("class", Token::CLASS) \
+ KEYWORD("class_name", Token::CLASS_NAME) \
+ KEYWORD("const", Token::CONST) \
+ KEYWORD("continue", Token::CONTINUE) \
+ KEYWORD_GROUP('e') \
+ KEYWORD("elif", Token::ELIF) \
+ KEYWORD("else", Token::ELSE) \
+ KEYWORD("enum", Token::ENUM) \
+ KEYWORD("extends", Token::EXTENDS) \
+ KEYWORD_GROUP('f') \
+ KEYWORD("for", Token::FOR) \
+ KEYWORD("func", Token::FUNC) \
+ KEYWORD_GROUP('i') \
+ KEYWORD("if", Token::IF) \
+ KEYWORD("in", Token::IN) \
+ KEYWORD("is", Token::IS) \
+ KEYWORD_GROUP('m') \
+ KEYWORD("match", Token::MATCH) \
+ KEYWORD_GROUP('n') \
+ KEYWORD("namespace", Token::NAMESPACE) \
+ KEYWORD("not", Token::NOT) \
+ KEYWORD_GROUP('o') \
+ KEYWORD("or", Token::OR) \
+ KEYWORD_GROUP('p') \
+ KEYWORD("pass", Token::PASS) \
+ KEYWORD("preload", Token::PRELOAD) \
+ KEYWORD_GROUP('r') \
+ KEYWORD("return", Token::RETURN) \
+ KEYWORD_GROUP('s') \
+ KEYWORD("self", Token::SELF) \
+ KEYWORD("signal", Token::SIGNAL) \
+ KEYWORD("static", Token::STATIC) \
+ KEYWORD("super", Token::SUPER) \
+ KEYWORD_GROUP('t') \
+ KEYWORD("trait", Token::TRAIT) \
+ KEYWORD_GROUP('v') \
+ KEYWORD("var", Token::VAR) \
+ KEYWORD("void", Token::VOID) \
+ KEYWORD_GROUP('w') \
+ KEYWORD("while", Token::WHILE) \
+ KEYWORD_GROUP('y') \
+ KEYWORD("yield", Token::YIELD) \
+ KEYWORD_GROUP('I') \
+ KEYWORD("INF", Token::CONST_INF) \
+ KEYWORD_GROUP('N') \
+ KEYWORD("NAN", Token::CONST_NAN) \
+ KEYWORD_GROUP('P') \
+ KEYWORD("PI", Token::CONST_PI) \
+ KEYWORD_GROUP('T') \
+ KEYWORD("TAU", Token::CONST_TAU)
+
+#define MIN_KEYWORD_LENGTH 2
+#define MAX_KEYWORD_LENGTH 10
+
+ // Consume all alphanumeric characters.
+ while (_is_alphanumeric(_peek())) {
+ _advance();
+ }
-int GDScriptTokenizerText::get_token_line(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, -1);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, -1);
+ int length = _current - _start;
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- return tk_rb[ofs].line;
-}
+ if (length == 1 && _peek(-1) == '_') {
+ // Lone underscore.
+ return make_token(Token::UNDERSCORE);
+ }
-int GDScriptTokenizerText::get_token_column(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, -1);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, -1);
+ String name(_start, length);
+ if (length < MIN_KEYWORD_LENGTH || length > MAX_KEYWORD_LENGTH) {
+ // Cannot be a keyword, as the length doesn't match any.
+ return make_identifier(name);
+ }
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- return tk_rb[ofs].col;
-}
+ // Define some helper macros for the switch case.
+#define KEYWORD_GROUP_CASE(char) \
+ break; \
+ case char:
+#define KEYWORD(keyword, token_type) \
+ { \
+ const int keyword_length = sizeof(keyword) - 1; \
+ static_assert(keyword_length <= MAX_KEYWORD_LENGTH, "There's a keyword longer than the defined maximum length"); \
+ static_assert(keyword_length >= MIN_KEYWORD_LENGTH, "There's a keyword shorter than the defined minimum length"); \
+ if (keyword_length == length && name == keyword) { \
+ return make_token(token_type); \
+ } \
+ }
-const Variant &GDScriptTokenizerText::get_token_constant(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, tk_rb[0].constant);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, tk_rb[0].constant);
+ // Find if it's a keyword.
+ switch (_start[0]) {
+ default:
+ KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
+ break;
+ }
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- ERR_FAIL_COND_V(tk_rb[ofs].type != TK_CONSTANT, tk_rb[0].constant);
- return tk_rb[ofs].constant;
-}
+ // Check if it's a special literal
+ if (length == 4) {
+ if (name == "true") {
+ return make_literal(true);
+ } else if (name == "null") {
+ return make_literal(Variant());
+ }
+ } else if (length == 5) {
+ if (name == "false") {
+ return make_literal(false);
+ }
+ }
-StringName GDScriptTokenizerText::get_token_identifier(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, StringName());
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, StringName());
+ // Not a keyword, so must be an identifier.
+ return make_identifier(name);
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- ERR_FAIL_COND_V(tk_rb[ofs].type != TK_IDENTIFIER, StringName());
- return tk_rb[ofs].identifier;
+#undef KEYWORDS
+#undef MIN_KEYWORD_LENGTH
+#undef MAX_KEYWORD_LENGTH
+#undef KEYWORD_GROUP_CASE
+#undef KEYWORD
}
-GDScriptFunctions::Function GDScriptTokenizerText::get_token_built_in_func(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, GDScriptFunctions::FUNC_MAX);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, GDScriptFunctions::FUNC_MAX);
+void GDScriptTokenizer::newline(bool p_make_token) {
+ // Don't overwrite previous newline, nor create if we want a line continuation.
+ if (p_make_token && !pending_newline && !line_continuation) {
+ Token newline(Token::NEWLINE);
+ newline.start_line = line;
+ newline.end_line = line;
+ newline.start_column = column - 1;
+ newline.end_column = column;
+ newline.leftmost_column = newline.start_column;
+ newline.rightmost_column = newline.end_column;
+ pending_newline = true;
+ last_newline = newline;
+ }
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- ERR_FAIL_COND_V(tk_rb[ofs].type != TK_BUILT_IN_FUNC, GDScriptFunctions::FUNC_MAX);
- return tk_rb[ofs].func;
+ // Increment line/column counters.
+ line++;
+ column = 1;
+ leftmost_column = 1;
}
-Variant::Type GDScriptTokenizerText::get_token_type(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, Variant::NIL);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, Variant::NIL);
+GDScriptTokenizer::Token GDScriptTokenizer::number() {
+ int base = 10;
+ bool has_decimal = false;
+ bool has_exponent = false;
+ bool has_error = false;
+ bool (*digit_check_func)(char32_t) = _is_digit;
+
+ if (_peek(-1) == '.') {
+ has_decimal = true;
+ } else if (_peek(-1) == '0') {
+ if (_peek() == 'x') {
+ // Hexadecimal.
+ base = 16;
+ digit_check_func = _is_hex_digit;
+ _advance();
+ } else if (_peek() == 'b') {
+ // Binary.
+ base = 2;
+ digit_check_func = _is_binary_digit;
+ _advance();
+ }
+ }
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- ERR_FAIL_COND_V(tk_rb[ofs].type != TK_BUILT_IN_TYPE, Variant::NIL);
- return tk_rb[ofs].vtype;
-}
+ // Allow '_' to be used in a number, for readability.
+ bool previous_was_underscore = false;
+ while (digit_check_func(_peek()) || _peek() == '_') {
+ if (_peek() == '_') {
+ if (previous_was_underscore) {
+ Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ }
+ previous_was_underscore = true;
+ }
+ _advance();
+ }
-int GDScriptTokenizerText::get_token_line_indent(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, 0);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, 0);
+ // It might be a ".." token (instead of decimal point) so we check if it's not.
+ if (_peek() == '.' && _peek(1) != '.') {
+ if (base == 10 && !has_decimal) {
+ has_decimal = true;
+ } else if (base == 10) {
+ Token error = make_error("Cannot use a decimal point twice in a number.");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ has_error = true;
+ } else if (base == 16) {
+ Token error = make_error("Cannot use a decimal point in a hexadecimal number.");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ has_error = true;
+ } else {
+ Token error = make_error("Cannot use a decimal point in a binary number.");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ has_error = true;
+ }
+ if (!has_error) {
+ _advance();
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- ERR_FAIL_COND_V(tk_rb[ofs].type != TK_NEWLINE, 0);
- return tk_rb[ofs].constant.operator Vector2().x;
-}
+ // Consume decimal digits.
+ while (_is_digit(_peek()) || _peek() == '_') {
+ _advance();
+ }
+ }
+ }
+ if (base == 10) {
+ if (_peek() == 'e' || _peek() == 'E') {
+ has_exponent = true;
+ _advance();
+ if (_peek() == '+' || _peek() == '-') {
+ // Exponent sign.
+ _advance();
+ }
+ // Consume exponent digits.
+ if (!_is_digit(_peek())) {
+ Token error = make_error(R"(Expected exponent value after "e".)");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ }
+ previous_was_underscore = false;
+ while (_is_digit(_peek()) || _peek() == '_') {
+ if (_peek() == '_') {
+ if (previous_was_underscore) {
+ Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ }
+ previous_was_underscore = true;
+ }
+ _advance();
+ }
+ }
+ }
-int GDScriptTokenizerText::get_token_line_tab_indent(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, 0);
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, 0);
+ // Detect extra decimal point.
+ if (!has_error && has_decimal && _peek() == '.' && _peek(1) != '.') {
+ Token error = make_error("Cannot use a decimal point twice in a number.");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ has_error = true;
+ } else if (_is_alphanumeric(_peek())) {
+ // Letter at the end of the number.
+ push_error("Invalid numeric notation.");
+ }
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- ERR_FAIL_COND_V(tk_rb[ofs].type != TK_NEWLINE, 0);
- return tk_rb[ofs].constant.operator Vector2().y;
+ // Create a string with the whole number.
+ int length = _current - _start;
+ String number = String(_start, length).replace("_", "");
+
+ // Convert to the appropriate literal type.
+ if (base == 16) {
+ int64_t value = number.hex_to_int();
+ return make_literal(value);
+ } else if (base == 2) {
+ int64_t value = number.bin_to_int();
+ return make_literal(value);
+ } else if (has_decimal || has_exponent) {
+ double value = number.to_float();
+ return make_literal(value);
+ } else {
+ int64_t value = number.to_int();
+ return make_literal(value);
+ }
}
-String GDScriptTokenizerText::get_token_error(int p_offset) const {
- ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, String());
- ERR_FAIL_COND_V(p_offset >= MAX_LOOKAHEAD, String());
+GDScriptTokenizer::Token GDScriptTokenizer::string() {
+ enum StringType {
+ STRING_REGULAR,
+ STRING_NAME,
+ STRING_NODEPATH,
+ };
- int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
- ERR_FAIL_COND_V(tk_rb[ofs].type != TK_ERROR, String());
- return tk_rb[ofs].constant;
-}
+ bool is_multiline = false;
+ StringType type = STRING_REGULAR;
-void GDScriptTokenizerText::advance(int p_amount) {
- ERR_FAIL_COND(p_amount <= 0);
- for (int i = 0; i < p_amount; i++) {
+ if (_peek(-1) == '&') {
+ type = STRING_NAME;
+ _advance();
+ } else if (_peek(-1) == '^') {
+ type = STRING_NODEPATH;
_advance();
}
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////
-#define BYTECODE_VERSION 13
+ char32_t quote_char = _peek(-1);
-Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) {
- const uint8_t *buf = p_buffer.ptr();
- int total_len = p_buffer.size();
- ERR_FAIL_COND_V(p_buffer.size() < 24 || p_buffer[0] != 'G' || p_buffer[1] != 'D' || p_buffer[2] != 'S' || p_buffer[3] != 'C', ERR_INVALID_DATA);
-
- int version = decode_uint32(&buf[4]);
- ERR_FAIL_COND_V_MSG(version > BYTECODE_VERSION, ERR_INVALID_DATA, "Bytecode is too recent! Please use a newer engine version.");
-
- int identifier_count = decode_uint32(&buf[8]);
- int constant_count = decode_uint32(&buf[12]);
- int line_count = decode_uint32(&buf[16]);
- int token_count = decode_uint32(&buf[20]);
+ if (_peek() == quote_char && _peek(1) == quote_char) {
+ is_multiline = true;
+ // Consume all quotes.
+ _advance();
+ _advance();
+ }
- const uint8_t *b = &buf[24];
- total_len -= 24;
+ String result;
- identifiers.resize(identifier_count);
- for (int i = 0; i < identifier_count; i++) {
- int len = decode_uint32(b);
- ERR_FAIL_COND_V(len > total_len, ERR_INVALID_DATA);
- b += 4;
- Vector<uint8_t> cs;
- cs.resize(len);
- for (int j = 0; j < len; j++) {
- cs.write[j] = b[j] ^ 0xb6;
+ for (;;) {
+ // Consume actual string.
+ if (_is_at_end()) {
+ return make_error("Unterminated string.");
}
- cs.write[cs.size() - 1] = 0;
- String s;
- s.parse_utf8((const char *)cs.ptr());
- b += len;
- total_len -= len + 4;
- identifiers.write[i] = s;
- }
+ char32_t ch = _peek();
- constants.resize(constant_count);
- for (int i = 0; i < constant_count; i++) {
- Variant v;
- int len;
- // An object cannot be constant, never decode objects
- Error err = decode_variant(v, b, total_len, &len, false);
- if (err) {
- return err;
- }
- b += len;
- total_len -= len;
- constants.write[i] = v;
- }
+ if (ch == '\\') {
+ // Escape pattern.
+ _advance();
+ if (_is_at_end()) {
+ return make_error("Unterminated string.");
+ }
- ERR_FAIL_COND_V(line_count * 8 > total_len, ERR_INVALID_DATA);
+ // Grab escape character.
+ char32_t code = _peek();
+ _advance();
+ if (_is_at_end()) {
+ return make_error("Unterminated string.");
+ }
- for (int i = 0; i < line_count; i++) {
- uint32_t token = decode_uint32(b);
- b += 4;
- uint32_t linecol = decode_uint32(b);
- b += 4;
+ char32_t escaped = 0;
+ bool valid_escape = true;
- lines.insert(token, linecol);
- total_len -= 8;
- }
+ switch (code) {
+ case 'a':
+ escaped = '\a';
+ break;
+ case 'b':
+ escaped = '\b';
+ break;
+ case 'f':
+ escaped = '\f';
+ break;
+ case 'n':
+ escaped = '\n';
+ break;
+ case 'r':
+ escaped = '\r';
+ break;
+ case 't':
+ escaped = '\t';
+ break;
+ case 'v':
+ escaped = '\v';
+ break;
+ case '\'':
+ escaped = '\'';
+ break;
+ case '\"':
+ escaped = '\"';
+ break;
+ case '\\':
+ escaped = '\\';
+ break;
+ case 'u':
+ // Hexadecimal sequence.
+ for (int i = 0; i < 4; i++) {
+ if (_is_at_end()) {
+ return make_error("Unterminated string.");
+ }
- tokens.resize(token_count);
+ char32_t digit = _peek();
+ char32_t value = 0;
+ if (digit >= '0' && digit <= '9') {
+ value = digit - '0';
+ } else if (digit >= 'a' && digit <= 'f') {
+ value = digit - 'a';
+ value += 10;
+ } else if (digit >= 'A' && digit <= 'F') {
+ value = digit - 'A';
+ value += 10;
+ } else {
+ // Make error, but keep parsing the string.
+ Token error = make_error("Invalid hexadecimal digit in unicode escape sequence.");
+ error.start_column = column;
+ error.leftmost_column = error.start_column;
+ error.end_column = column + 1;
+ error.rightmost_column = error.end_column;
+ push_error(error);
+ valid_escape = false;
+ break;
+ }
- for (int i = 0; i < token_count; i++) {
- ERR_FAIL_COND_V(total_len < 1, ERR_INVALID_DATA);
+ escaped <<= 4;
+ escaped |= value;
- if ((*b) & TOKEN_BYTE_MASK) { //little endian always
- ERR_FAIL_COND_V(total_len < 4, ERR_INVALID_DATA);
+ _advance();
+ }
+ break;
+ case '\r':
+ if (_peek() != '\n') {
+ // Carriage return without newline in string. (???)
+ // Just add it to the string and keep going.
+ result += ch;
+ _advance();
+ break;
+ }
+ [[fallthrough]];
+ case '\n':
+ // Escaping newline.
+ newline(false);
+ valid_escape = false; // Don't add to the string.
+ break;
+ default:
+ Token error = make_error("Invalid escape in string.");
+ error.start_column = column - 2;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ valid_escape = false;
+ break;
+ }
- tokens.write[i] = decode_uint32(b) & ~TOKEN_BYTE_MASK;
- b += 4;
+ if (valid_escape) {
+ result += escaped;
+ }
+ } else if (ch == quote_char) {
+ _advance();
+ if (is_multiline) {
+ if (_peek() == quote_char && _peek(1) == quote_char) {
+ // Ended the multiline string. Consume all quotes.
+ _advance();
+ _advance();
+ break;
+ }
+ } else {
+ // Ended single-line string.
+ break;
+ }
} else {
- tokens.write[i] = *b;
- b += 1;
- total_len--;
+ result += ch;
+ _advance();
+ if (ch == '\n') {
+ newline(false);
+ }
}
}
- token = 0;
+ // Make the literal.
+ Variant string;
+ switch (type) {
+ case STRING_NAME:
+ string = StringName(result);
+ break;
+ case STRING_NODEPATH:
+ string = NodePath(result);
+ break;
+ case STRING_REGULAR:
+ string = result;
+ break;
+ }
- return OK;
+ return make_literal(string);
}
-Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) {
- Vector<uint8_t> buf;
+void GDScriptTokenizer::check_indent() {
+ ERR_FAIL_COND_MSG(column != 1, "Checking tokenizer indentation in the middle of a line.");
- Map<StringName, int> identifier_map;
- HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
- Map<uint32_t, int> line_map;
- Vector<uint32_t> token_array;
+ if (_is_at_end()) {
+ // Send dedents for every indent level.
+ pending_indents -= indent_level();
+ indent_stack.clear();
+ return;
+ }
- GDScriptTokenizerText tt;
- tt.set_code(p_code);
- int line = -1;
+ for (;;) {
+ char32_t current_indent_char = _peek();
+ int indent_count = 0;
- while (true) {
- if (tt.get_token_line() != line) {
- line = tt.get_token_line();
- line_map[line] = token_array.size();
+ if (current_indent_char != ' ' && current_indent_char != '\t' && current_indent_char != '\r' && current_indent_char != '\n' && current_indent_char != '#') {
+ // First character of the line is not whitespace, so we clear all indentation levels.
+ // Unless we are in a continuation or in multiline mode (inside expression).
+ if (line_continuation || multiline_mode) {
+ return;
+ }
+ pending_indents -= indent_level();
+ indent_stack.clear();
+ return;
}
- uint32_t token = tt.get_token();
- switch (tt.get_token()) {
- case TK_IDENTIFIER: {
- StringName id = tt.get_token_identifier();
- if (!identifier_map.has(id)) {
- int idx = identifier_map.size();
- identifier_map[id] = idx;
- }
- token |= identifier_map[id] << TOKEN_BITS;
- } break;
- case TK_CONSTANT: {
- const Variant &c = tt.get_token_constant();
- if (!constant_map.has(c)) {
- int idx = constant_map.size();
- constant_map[c] = idx;
- }
- token |= constant_map[c] << TOKEN_BITS;
- } break;
- case TK_BUILT_IN_TYPE: {
- token |= tt.get_token_type() << TOKEN_BITS;
- } break;
- case TK_BUILT_IN_FUNC: {
- token |= tt.get_token_built_in_func() << TOKEN_BITS;
-
- } break;
- case TK_NEWLINE: {
- token |= tt.get_token_line_indent() << TOKEN_BITS;
- } break;
- case TK_ERROR: {
- ERR_FAIL_V(Vector<uint8_t>());
- } break;
- default: {
+ if (_peek() == '\r') {
+ _advance();
+ if (_peek() != '\n') {
+ push_error("Stray carriage return character in source code.");
}
- };
-
- token_array.push_back(token);
-
- if (tt.get_token() == TK_EOF) {
- break;
}
- tt.advance();
- }
-
- //reverse maps
-
- Map<int, StringName> rev_identifier_map;
- for (Map<StringName, int>::Element *E = identifier_map.front(); E; E = E->next()) {
- rev_identifier_map[E->get()] = E->key();
- }
+ if (_peek() == '\n') {
+ // Empty line, keep going.
+ _advance();
+ newline(false);
+ continue;
+ }
- Map<int, Variant> rev_constant_map;
- const Variant *K = nullptr;
- while ((K = constant_map.next(K))) {
- rev_constant_map[constant_map[*K]] = *K;
- }
+ // Check indent level.
+ bool mixed = false;
+ while (!_is_at_end()) {
+ char32_t space = _peek();
+ if (space == '\t') {
+ // Consider individual tab columns.
+ column += tab_size - 1;
+ indent_count += tab_size;
+ } else if (space == ' ') {
+ indent_count += 1;
+ } else {
+ break;
+ }
+ mixed = mixed || space != current_indent_char;
+ _advance();
+ }
- Map<int, uint32_t> rev_line_map;
- for (Map<uint32_t, int>::Element *E = line_map.front(); E; E = E->next()) {
- rev_line_map[E->get()] = E->key();
- }
+ if (mixed) {
+ Token error = make_error("Mixed use of tabs and spaces for indentation.");
+ error.start_line = line;
+ error.start_column = 1;
+ error.leftmost_column = 1;
+ error.rightmost_column = column;
+ push_error(error);
+ }
- //save header
- buf.resize(24);
- buf.write[0] = 'G';
- buf.write[1] = 'D';
- buf.write[2] = 'S';
- buf.write[3] = 'C';
- encode_uint32(BYTECODE_VERSION, &buf.write[4]);
- encode_uint32(identifier_map.size(), &buf.write[8]);
- encode_uint32(constant_map.size(), &buf.write[12]);
- encode_uint32(line_map.size(), &buf.write[16]);
- encode_uint32(token_array.size(), &buf.write[20]);
-
- //save identifiers
-
- for (Map<int, StringName>::Element *E = rev_identifier_map.front(); E; E = E->next()) {
- CharString cs = String(E->get()).utf8();
- int len = cs.length() + 1;
- int extra = 4 - (len % 4);
- if (extra == 4) {
- extra = 0;
+ if (_is_at_end()) {
+ // Reached the end with an empty line, so just dedent as much as needed.
+ pending_indents -= indent_level();
+ indent_stack.clear();
+ return;
}
- uint8_t ibuf[4];
- encode_uint32(len + extra, ibuf);
- for (int i = 0; i < 4; i++) {
- buf.push_back(ibuf[i]);
+ if (_peek() == '\r') {
+ _advance();
+ if (_peek() != '\n') {
+ push_error("Stray carriage return character in source code.");
+ }
}
- for (int i = 0; i < len; i++) {
- buf.push_back(cs[i] ^ 0xb6);
+ if (_peek() == '\n') {
+ // Empty line, keep going.
+ _advance();
+ newline(false);
+ continue;
}
- for (int i = 0; i < extra; i++) {
- buf.push_back(0 ^ 0xb6);
+ if (_peek() == '#') {
+ // Comment. Advance to the next line.
+ while (_peek() != '\n' && !_is_at_end()) {
+ _advance();
+ }
+ if (_is_at_end()) {
+ // Reached the end with an empty line, so just dedent as much as needed.
+ pending_indents -= indent_level();
+ indent_stack.clear();
+ return;
+ }
+ _advance(); // Consume '\n'.
+ newline(false);
+ continue;
}
- }
- for (Map<int, Variant>::Element *E = rev_constant_map.front(); E; E = E->next()) {
- int len;
- // Objects cannot be constant, never encode objects
- Error err = encode_variant(E->get(), nullptr, len, false);
- ERR_FAIL_COND_V_MSG(err != OK, Vector<uint8_t>(), "Error when trying to encode Variant.");
- int pos = buf.size();
- buf.resize(pos + len);
- encode_variant(E->get(), &buf.write[pos], len, false);
- }
+ if (line_continuation || multiline_mode) {
+ // We cleared up all the whitespace at the beginning of the line.
+ // But if this is a continuation or multiline mode and we don't want any indentation change.
+ return;
+ }
- for (Map<int, uint32_t>::Element *E = rev_line_map.front(); E; E = E->next()) {
- uint8_t ibuf[8];
- encode_uint32(E->key(), &ibuf[0]);
- encode_uint32(E->get(), &ibuf[4]);
- for (int i = 0; i < 8; i++) {
- buf.push_back(ibuf[i]);
+ // Check if indentation character is consistent.
+ if (indent_char == '\0') {
+ // First time indenting, choose character now.
+ indent_char = current_indent_char;
+ } else if (current_indent_char != indent_char) {
+ Token error = make_error(vformat("Used \"%s\" for indentation instead \"%s\" as used before in the file.", String(&current_indent_char, 1).c_escape(), String(&indent_char, 1).c_escape()));
+ error.start_line = line;
+ error.start_column = 1;
+ error.leftmost_column = 1;
+ error.rightmost_column = column;
+ push_error(error);
}
- }
- for (int i = 0; i < token_array.size(); i++) {
- uint32_t token = token_array[i];
+ // Now we can do actual indentation changes.
- if (token & ~TOKEN_MASK) {
- uint8_t buf4[4];
- encode_uint32(token_array[i] | TOKEN_BYTE_MASK, &buf4[0]);
- for (int j = 0; j < 4; j++) {
- buf.push_back(buf4[j]);
- }
+ // Check if indent or dedent.
+ int previous_indent = 0;
+ if (indent_level() > 0) {
+ previous_indent = indent_stack.back()->get();
+ }
+ if (indent_count == previous_indent) {
+ // No change in indentation.
+ return;
+ }
+ if (indent_count > previous_indent) {
+ // Indentation increased.
+ indent_stack.push_back(indent_count);
+ pending_indents++;
} else {
- buf.push_back(token);
+ // Indentation decreased (dedent).
+ if (indent_level() == 0) {
+ push_error("Tokenizer bug: trying to dedent without previous indent.");
+ return;
+ }
+ while (indent_level() > 0 && indent_stack.back()->get() > indent_count) {
+ indent_stack.pop_back();
+ pending_indents--;
+ }
+ if ((indent_level() > 0 && indent_stack.back()->get() != indent_count) || (indent_level() == 0 && indent_count != 0)) {
+ // Mismatched indentation alignment.
+ Token error = make_error("Unindent doesn't match the previous indentation level.");
+ error.start_line = line;
+ error.start_column = 1;
+ error.leftmost_column = 1;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ // Still, we'll be lenient and keep going, so keep this level in the stack.
+ indent_stack.push_back(indent_count);
+ }
}
+ break; // Get out of the loop in any case.
}
-
- return buf;
}
-GDScriptTokenizerBuffer::Token GDScriptTokenizerBuffer::get_token(int p_offset) const {
- int offset = token + p_offset;
-
- if (offset < 0 || offset >= tokens.size()) {
- return TK_EOF;
+void GDScriptTokenizer::_skip_whitespace() {
+ if (pending_indents != 0) {
+ // Still have some indent/dedent tokens to give.
+ return;
}
- return GDScriptTokenizerBuffer::Token(tokens[offset] & TOKEN_MASK);
-}
-
-StringName GDScriptTokenizerBuffer::get_token_identifier(int p_offset) const {
- int offset = token + p_offset;
+ bool is_bol = column == 1; // Beginning of line.
- ERR_FAIL_INDEX_V(offset, tokens.size(), StringName());
- uint32_t identifier = tokens[offset] >> TOKEN_BITS;
- ERR_FAIL_UNSIGNED_INDEX_V(identifier, (uint32_t)identifiers.size(), StringName());
+ if (is_bol) {
+ check_indent();
+ return;
+ }
- return identifiers[identifier];
+ for (;;) {
+ char32_t c = _peek();
+ switch (c) {
+ case ' ':
+ _advance();
+ break;
+ case '\t':
+ _advance();
+ // Consider individual tab columns.
+ column += tab_size - 1;
+ break;
+ case '\r':
+ _advance(); // Consume either way.
+ if (_peek() != '\n') {
+ push_error("Stray carriage return character in source code.");
+ return;
+ }
+ break;
+ case '\n':
+ _advance();
+ newline(!is_bol); // Don't create new line token if line is empty.
+ check_indent();
+ break;
+ case '#':
+ // Comment.
+ while (_peek() != '\n' && !_is_at_end()) {
+ _advance();
+ }
+ if (_is_at_end()) {
+ return;
+ }
+ _advance(); // Consume '\n'
+ newline(!is_bol);
+ check_indent();
+ break;
+ default:
+ return;
+ }
+ }
}
-GDScriptFunctions::Function GDScriptTokenizerBuffer::get_token_built_in_func(int p_offset) const {
- int offset = token + p_offset;
- ERR_FAIL_INDEX_V(offset, tokens.size(), GDScriptFunctions::FUNC_MAX);
- return GDScriptFunctions::Function(tokens[offset] >> TOKEN_BITS);
-}
+GDScriptTokenizer::Token GDScriptTokenizer::scan() {
+ if (has_error()) {
+ return pop_error();
+ }
-Variant::Type GDScriptTokenizerBuffer::get_token_type(int p_offset) const {
- int offset = token + p_offset;
- ERR_FAIL_INDEX_V(offset, tokens.size(), Variant::NIL);
+ _skip_whitespace();
- return Variant::Type(tokens[offset] >> TOKEN_BITS);
-}
+ if (pending_newline) {
+ pending_newline = false;
+ if (!multiline_mode) {
+ // Don't return newline tokens on multine mode.
+ return last_newline;
+ }
+ }
-int GDScriptTokenizerBuffer::get_token_line(int p_offset) const {
- int offset = token + p_offset;
- int pos = lines.find_nearest(offset);
+ // Check for potential errors after skipping whitespace().
+ if (has_error()) {
+ return pop_error();
+ }
- if (pos < 0) {
- return -1;
+ _start = _current;
+ start_line = line;
+ start_column = column;
+ leftmost_column = column;
+ rightmost_column = column;
+
+ if (pending_indents != 0) {
+ // Adjust position for indent.
+ _start -= start_column - 1;
+ start_column = 1;
+ leftmost_column = 1;
+ if (pending_indents > 0) {
+ // Indents.
+ pending_indents--;
+ return make_token(Token::INDENT);
+ } else {
+ // Dedents.
+ pending_indents++;
+ Token dedent = make_token(Token::DEDENT);
+ dedent.end_column += 1;
+ dedent.rightmost_column += 1;
+ return dedent;
+ }
}
- if (pos >= lines.size()) {
- pos = lines.size() - 1;
+
+ if (_is_at_end()) {
+ return make_token(Token::TK_EOF);
}
- uint32_t l = lines.getv(pos);
- return l & TOKEN_LINE_MASK;
-}
+ const char32_t c = _advance();
-int GDScriptTokenizerBuffer::get_token_column(int p_offset) const {
- int offset = token + p_offset;
- int pos = lines.find_nearest(offset);
- if (pos < 0) {
- return -1;
- }
- if (pos >= lines.size()) {
- pos = lines.size() - 1;
+ if (c == '\\') {
+ // Line continuation with backslash.
+ if (_peek() == '\r') {
+ if (_peek(1) != '\n') {
+ return make_error("Unexpected carriage return character.");
+ }
+ _advance();
+ }
+ if (_peek() != '\n') {
+ return make_error("Expected new line after \"\\\".");
+ }
+ _advance();
+ newline(false);
+ line_continuation = true;
+ return scan(); // Recurse to get next token.
}
- uint32_t l = lines.getv(pos);
- return l >> TOKEN_LINE_BITS;
-}
+ line_continuation = false;
-int GDScriptTokenizerBuffer::get_token_line_indent(int p_offset) const {
- int offset = token + p_offset;
- ERR_FAIL_INDEX_V(offset, tokens.size(), 0);
- return tokens[offset] >> TOKEN_BITS;
-}
+ if (_is_digit(c)) {
+ return number();
+ } else if (_is_alphanumeric(c)) {
+ return potential_identifier();
+ }
-const Variant &GDScriptTokenizerBuffer::get_token_constant(int p_offset) const {
- int offset = token + p_offset;
- ERR_FAIL_INDEX_V(offset, tokens.size(), nil);
- uint32_t constant = tokens[offset] >> TOKEN_BITS;
- ERR_FAIL_UNSIGNED_INDEX_V(constant, (uint32_t)constants.size(), nil);
- return constants[constant];
-}
+ switch (c) {
+ // String literals.
+ case '"':
+ case '\'':
+ return string();
+
+ // Annotation.
+ case '@':
+ return annotation();
+
+ // Single characters.
+ case '~':
+ return make_token(Token::TILDE);
+ case ',':
+ return make_token(Token::COMMA);
+ case ':':
+ return make_token(Token::COLON);
+ case ';':
+ return make_token(Token::SEMICOLON);
+ case '$':
+ return make_token(Token::DOLLAR);
+ case '?':
+ return make_token(Token::QUESTION_MARK);
+ case '`':
+ return make_token(Token::BACKTICK);
+
+ // Parens.
+ case '(':
+ push_paren('(');
+ return make_token(Token::PARENTHESIS_OPEN);
+ case '[':
+ push_paren('[');
+ return make_token(Token::BRACKET_OPEN);
+ case '{':
+ push_paren('{');
+ return make_token(Token::BRACE_OPEN);
+ case ')':
+ if (!pop_paren('(')) {
+ return make_paren_error(c);
+ }
+ return make_token(Token::PARENTHESIS_CLOSE);
+ case ']':
+ if (!pop_paren('[')) {
+ return make_paren_error(c);
+ }
+ return make_token(Token::BRACKET_CLOSE);
+ case '}':
+ if (!pop_paren('{')) {
+ return make_paren_error(c);
+ }
+ return make_token(Token::BRACE_CLOSE);
+
+ // Double characters.
+ case '!':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::BANG_EQUAL);
+ } else {
+ return make_token(Token::BANG);
+ }
+ case '.':
+ if (_peek() == '.') {
+ _advance();
+ return make_token(Token::PERIOD_PERIOD);
+ } else if (_is_digit(_peek())) {
+ // Number starting with '.'.
+ return number();
+ } else {
+ return make_token(Token::PERIOD);
+ }
+ case '+':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::PLUS_EQUAL);
+ } else {
+ return make_token(Token::PLUS);
+ }
+ case '-':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::MINUS_EQUAL);
+ } else if (_peek() == '>') {
+ _advance();
+ return make_token(Token::FORWARD_ARROW);
+ } else {
+ return make_token(Token::MINUS);
+ }
+ case '*':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::STAR_EQUAL);
+ } else {
+ return make_token(Token::STAR);
+ }
+ case '/':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::SLASH_EQUAL);
+ } else {
+ return make_token(Token::SLASH);
+ }
+ case '%':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::PERCENT_EQUAL);
+ } else {
+ return make_token(Token::PERCENT);
+ }
+ case '^':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::CARET_EQUAL);
+ } else if (_peek() == '"' || _peek() == '\'') {
+ // Node path
+ return string();
+ } else {
+ return make_token(Token::CARET);
+ }
+ case '&':
+ if (_peek() == '&') {
+ _advance();
+ return make_token(Token::AMPERSAND_AMPERSAND);
+ } else if (_peek() == '=') {
+ _advance();
+ return make_token(Token::AMPERSAND_EQUAL);
+ } else if (_peek() == '"' || _peek() == '\'') {
+ // String Name
+ return string();
+ } else {
+ return make_token(Token::AMPERSAND);
+ }
+ case '|':
+ if (_peek() == '|') {
+ _advance();
+ return make_token(Token::PIPE_PIPE);
+ } else if (_peek() == '=') {
+ _advance();
+ return make_token(Token::PIPE_EQUAL);
+ } else {
+ return make_token(Token::PIPE);
+ }
-String GDScriptTokenizerBuffer::get_token_error(int p_offset) const {
- ERR_FAIL_V(String());
-}
+ // Potential VCS conflict markers.
+ case '=':
+ if (_peek() == '=') {
+ return check_vcs_marker('=', Token::EQUAL_EQUAL);
+ } else {
+ return make_token(Token::EQUAL);
+ }
+ case '<':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::LESS_EQUAL);
+ } else if (_peek() == '<') {
+ if (_peek(1) == '=') {
+ _advance();
+ _advance(); // Advance both '<' and '='
+ return make_token(Token::LESS_LESS_EQUAL);
+ } else {
+ return check_vcs_marker('<', Token::LESS_LESS);
+ }
+ } else {
+ return make_token(Token::LESS);
+ }
+ case '>':
+ if (_peek() == '=') {
+ _advance();
+ return make_token(Token::GREATER_EQUAL);
+ } else if (_peek() == '>') {
+ if (_peek(1) == '=') {
+ _advance();
+ _advance(); // Advance both '>' and '='
+ return make_token(Token::GREATER_GREATER_EQUAL);
+ } else {
+ return check_vcs_marker('>', Token::GREATER_GREATER);
+ }
+ } else {
+ return make_token(Token::GREATER);
+ }
-void GDScriptTokenizerBuffer::advance(int p_amount) {
- ERR_FAIL_INDEX(p_amount + token, tokens.size());
- token += p_amount;
+ default:
+ return make_error(vformat(R"(Unknown character "%s".")", String(&c, 1)));
+ }
}
-GDScriptTokenizerBuffer::GDScriptTokenizerBuffer() {
- token = 0;
+GDScriptTokenizer::GDScriptTokenizer() {
+#ifdef TOOLS_ENABLED
+ if (EditorSettings::get_singleton()) {
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ }
+#endif // TOOLS_ENABLED
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 32603c010f..4453982d08 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -31,268 +31,221 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
-#include "core/pair.h"
+#include "core/list.h"
#include "core/set.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
#include "core/variant.h"
-#include "core/vmap.h"
-#include "gdscript_functions.h"
+#include "core/vector.h"
class GDScriptTokenizer {
public:
- enum Token {
-
- TK_EMPTY,
- TK_IDENTIFIER,
- TK_CONSTANT,
- TK_SELF,
- TK_BUILT_IN_TYPE,
- TK_BUILT_IN_FUNC,
- TK_OP_IN,
- TK_OP_EQUAL,
- TK_OP_NOT_EQUAL,
- TK_OP_LESS,
- TK_OP_LESS_EQUAL,
- TK_OP_GREATER,
- TK_OP_GREATER_EQUAL,
- TK_OP_AND,
- TK_OP_OR,
- TK_OP_NOT,
- TK_OP_ADD,
- TK_OP_SUB,
- TK_OP_MUL,
- TK_OP_DIV,
- TK_OP_MOD,
- TK_OP_SHIFT_LEFT,
- TK_OP_SHIFT_RIGHT,
- TK_OP_ASSIGN,
- TK_OP_ASSIGN_ADD,
- TK_OP_ASSIGN_SUB,
- TK_OP_ASSIGN_MUL,
- TK_OP_ASSIGN_DIV,
- TK_OP_ASSIGN_MOD,
- TK_OP_ASSIGN_SHIFT_LEFT,
- TK_OP_ASSIGN_SHIFT_RIGHT,
- TK_OP_ASSIGN_BIT_AND,
- TK_OP_ASSIGN_BIT_OR,
- TK_OP_ASSIGN_BIT_XOR,
- TK_OP_BIT_AND,
- TK_OP_BIT_OR,
- TK_OP_BIT_XOR,
- TK_OP_BIT_INVERT,
- //TK_OP_PLUS_PLUS,
- //TK_OP_MINUS_MINUS,
- TK_CF_IF,
- TK_CF_ELIF,
- TK_CF_ELSE,
- TK_CF_FOR,
- TK_CF_WHILE,
- TK_CF_BREAK,
- TK_CF_CONTINUE,
- TK_CF_PASS,
- TK_CF_RETURN,
- TK_CF_MATCH,
- TK_PR_FUNCTION,
- TK_PR_CLASS,
- TK_PR_CLASS_NAME,
- TK_PR_EXTENDS,
- TK_PR_IS,
- TK_PR_ONREADY,
- TK_PR_TOOL,
- TK_PR_STATIC,
- TK_PR_EXPORT,
- TK_PR_SETGET,
- TK_PR_CONST,
- TK_PR_VAR,
- TK_PR_AS,
- TK_PR_VOID,
- TK_PR_ENUM,
- TK_PR_PRELOAD,
- TK_PR_ASSERT,
- TK_PR_YIELD,
- TK_PR_SIGNAL,
- TK_PR_BREAKPOINT,
- TK_PR_REMOTE,
- TK_PR_MASTER,
- TK_PR_PUPPET,
- TK_PR_REMOTESYNC,
- TK_PR_MASTERSYNC,
- TK_PR_PUPPETSYNC,
- TK_BRACKET_OPEN,
- TK_BRACKET_CLOSE,
- TK_CURLY_BRACKET_OPEN,
- TK_CURLY_BRACKET_CLOSE,
- TK_PARENTHESIS_OPEN,
- TK_PARENTHESIS_CLOSE,
- TK_COMMA,
- TK_SEMICOLON,
- TK_PERIOD,
- TK_QUESTION_MARK,
- TK_COLON,
- TK_DOLLAR,
- TK_FORWARD_ARROW,
- TK_NEWLINE,
- TK_CONST_PI,
- TK_CONST_TAU,
- TK_WILDCARD,
- TK_CONST_INF,
- TK_CONST_NAN,
- TK_ERROR,
- TK_EOF,
- TK_CURSOR, //used for code completion
- TK_MAX
- };
-
-protected:
- enum StringMode {
- STRING_SINGLE_QUOTE,
- STRING_DOUBLE_QUOTE,
- STRING_MULTILINE
+ enum CursorPlace {
+ CURSOR_NONE,
+ CURSOR_BEGINNING,
+ CURSOR_MIDDLE,
+ CURSOR_END,
};
- static const char *token_names[TK_MAX];
-
-public:
- static const char *get_token_name(Token p_token);
-
- bool is_token_literal(int p_offset = 0, bool variable_safe = false) const;
- StringName get_token_literal(int p_offset = 0) const;
-
- virtual const Variant &get_token_constant(int p_offset = 0) const = 0;
- virtual Token get_token(int p_offset = 0) const = 0;
- virtual StringName get_token_identifier(int p_offset = 0) const = 0;
- virtual GDScriptFunctions::Function get_token_built_in_func(int p_offset = 0) const = 0;
- virtual Variant::Type get_token_type(int p_offset = 0) const = 0;
- virtual int get_token_line(int p_offset = 0) const = 0;
- virtual int get_token_column(int p_offset = 0) const = 0;
- virtual int get_token_line_indent(int p_offset = 0) const = 0;
- virtual int get_token_line_tab_indent(int p_offset = 0) const = 0;
- virtual String get_token_error(int p_offset = 0) const = 0;
- virtual void advance(int p_amount = 1) = 0;
-#ifdef DEBUG_ENABLED
- virtual const Vector<Pair<int, String>> &get_warning_skips() const = 0;
- virtual const Set<String> &get_warning_global_skips() const = 0;
- virtual bool is_ignoring_warnings() const = 0;
-#endif // DEBUG_ENABLED
-
- virtual ~GDScriptTokenizer() {}
-};
-
-class GDScriptTokenizerText : public GDScriptTokenizer {
- enum {
- MAX_LOOKAHEAD = 4,
- TK_RB_SIZE = MAX_LOOKAHEAD * 2 + 1
+ struct Token {
+ enum Type {
+ EMPTY,
+ // Basic
+ ANNOTATION,
+ IDENTIFIER,
+ LITERAL,
+ // Comparison
+ LESS,
+ LESS_EQUAL,
+ GREATER,
+ GREATER_EQUAL,
+ EQUAL_EQUAL,
+ BANG_EQUAL,
+ // Logical
+ AND,
+ OR,
+ NOT,
+ AMPERSAND_AMPERSAND,
+ PIPE_PIPE,
+ BANG,
+ // Bitwise
+ AMPERSAND,
+ PIPE,
+ TILDE,
+ CARET,
+ LESS_LESS,
+ GREATER_GREATER,
+ // Math
+ PLUS,
+ MINUS,
+ STAR,
+ SLASH,
+ PERCENT,
+ // Assignment
+ EQUAL,
+ PLUS_EQUAL,
+ MINUS_EQUAL,
+ STAR_EQUAL,
+ SLASH_EQUAL,
+ PERCENT_EQUAL,
+ LESS_LESS_EQUAL,
+ GREATER_GREATER_EQUAL,
+ AMPERSAND_EQUAL,
+ PIPE_EQUAL,
+ CARET_EQUAL,
+ // Control flow
+ IF,
+ ELIF,
+ ELSE,
+ FOR,
+ WHILE,
+ BREAK,
+ CONTINUE,
+ PASS,
+ RETURN,
+ MATCH,
+ // Keywords
+ AS,
+ ASSERT,
+ AWAIT,
+ BREAKPOINT,
+ CLASS,
+ CLASS_NAME,
+ CONST,
+ ENUM,
+ EXTENDS,
+ FUNC,
+ IN,
+ IS,
+ NAMESPACE,
+ PRELOAD,
+ SELF,
+ SIGNAL,
+ STATIC,
+ SUPER,
+ TRAIT,
+ VAR,
+ VOID,
+ YIELD,
+ // Punctuation
+ BRACKET_OPEN,
+ BRACKET_CLOSE,
+ BRACE_OPEN,
+ BRACE_CLOSE,
+ PARENTHESIS_OPEN,
+ PARENTHESIS_CLOSE,
+ COMMA,
+ SEMICOLON,
+ PERIOD,
+ PERIOD_PERIOD,
+ COLON,
+ DOLLAR,
+ FORWARD_ARROW,
+ UNDERSCORE,
+ // Whitespace
+ NEWLINE,
+ INDENT,
+ DEDENT,
+ // Constants
+ CONST_PI,
+ CONST_TAU,
+ CONST_INF,
+ CONST_NAN,
+ // Error message improvement
+ VCS_CONFLICT_MARKER,
+ BACKTICK,
+ QUESTION_MARK,
+ // Special
+ ERROR,
+ TK_EOF, // "EOF" is reserved
+ TK_MAX
+ };
- };
+ Type type = EMPTY;
+ Variant literal;
+ int start_line = 0, end_line = 0, start_column = 0, end_column = 0;
+ int leftmost_column = 0, rightmost_column = 0; // Column span for multiline tokens.
+ int cursor_position = -1;
+ CursorPlace cursor_place = CURSOR_NONE;
+ String source;
+
+ const char *get_name() const;
+ bool is_identifier() const;
+ bool is_node_name() const;
+ StringName get_identifier() const { return source; }
+
+ Token(Type p_type) {
+ type = p_type;
+ }
- struct TokenData {
- Token type;
- StringName identifier; //for identifier types
- Variant constant; //for constant types
- union {
- Variant::Type vtype; //for type types
- GDScriptFunctions::Function func; //function for built in functions
- int warning_code; //for warning skip
- };
- int line, col;
- TokenData() {
- type = TK_EMPTY;
- line = col = 0;
- vtype = Variant::NIL;
+ Token() {
+ type = EMPTY;
}
};
- void _make_token(Token p_type);
- void _make_newline(int p_indentation = 0, int p_tabs = 0);
- void _make_identifier(const StringName &p_identifier);
- void _make_built_in_func(GDScriptFunctions::Function p_func);
- void _make_constant(const Variant &p_constant);
- void _make_type(const Variant::Type &p_type);
- void _make_error(const String &p_error);
-
- String code;
- int len;
- int code_pos;
- const CharType *_code;
- int line;
- int column;
- TokenData tk_rb[TK_RB_SIZE * 2 + 1];
- int tk_rb_pos;
- String last_error;
- bool error_flag;
-
-#ifdef DEBUG_ENABLED
- Vector<Pair<int, String>> warning_skips;
- Set<String> warning_global_skips;
- bool ignore_warnings;
-#endif // DEBUG_ENABLED
-
- void _advance();
+private:
+ String source;
+ const char32_t *_source = nullptr;
+ const char32_t *_current = nullptr;
+ int line = -1, column = -1;
+ int cursor_line = -1, cursor_column = -1;
+ int tab_size = 4;
+
+ // Keep track of multichar tokens.
+ const char32_t *_start = nullptr;
+ int start_line = 0, start_column = 0;
+ int leftmost_column = 0, rightmost_column = 0;
+
+ // Info cache.
+ bool line_continuation = false; // Whether this line is a continuation of the previous, like when using '\'.
+ bool multiline_mode = false;
+ List<Token> error_stack;
+ bool pending_newline = false;
+ Token last_newline;
+ int pending_indents = 0;
+ List<int> indent_stack;
+ List<char32_t> paren_stack;
+ char32_t indent_char = '\0';
+ int position = 0;
+ int length = 0;
+
+ _FORCE_INLINE_ bool _is_at_end() { return position >= length; }
+ _FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; }
+ int indent_level() const { return indent_stack.size(); }
+ bool has_error() const { return !error_stack.empty(); }
+ Token pop_error();
+ char32_t _advance();
+ void _skip_whitespace();
+ void check_indent();
+
+ Token make_error(const String &p_message);
+ void push_error(const String &p_message);
+ void push_error(const Token &p_error);
+ Token make_paren_error(char32_t p_paren);
+ Token make_token(Token::Type p_type);
+ Token make_literal(const Variant &p_literal);
+ Token make_identifier(const StringName &p_identifier);
+ Token check_vcs_marker(char32_t p_test, Token::Type p_double_type);
+ void push_paren(char32_t p_char);
+ bool pop_paren(char32_t p_expected);
+
+ void newline(bool p_make_token);
+ Token number();
+ Token potential_identifier();
+ Token string();
+ Token annotation();
public:
- void set_code(const String &p_code);
- virtual Token get_token(int p_offset = 0) const;
- virtual StringName get_token_identifier(int p_offset = 0) const;
- virtual GDScriptFunctions::Function get_token_built_in_func(int p_offset = 0) const;
- virtual Variant::Type get_token_type(int p_offset = 0) const;
- virtual int get_token_line(int p_offset = 0) const;
- virtual int get_token_column(int p_offset = 0) const;
- virtual int get_token_line_indent(int p_offset = 0) const;
- virtual int get_token_line_tab_indent(int p_offset = 0) const;
- virtual const Variant &get_token_constant(int p_offset = 0) const;
- virtual String get_token_error(int p_offset = 0) const;
- virtual void advance(int p_amount = 1);
-#ifdef DEBUG_ENABLED
- virtual const Vector<Pair<int, String>> &get_warning_skips() const { return warning_skips; }
- virtual const Set<String> &get_warning_global_skips() const { return warning_global_skips; }
- virtual bool is_ignoring_warnings() const { return ignore_warnings; }
-#endif // DEBUG_ENABLED
-};
+ Token scan();
-class GDScriptTokenizerBuffer : public GDScriptTokenizer {
- enum {
+ void set_source_code(const String &p_source_code);
- TOKEN_BYTE_MASK = 0x80,
- TOKEN_BITS = 8,
- TOKEN_MASK = (1 << TOKEN_BITS) - 1,
- TOKEN_LINE_BITS = 24,
- TOKEN_LINE_MASK = (1 << TOKEN_LINE_BITS) - 1,
- };
+ int get_cursor_line() const;
+ int get_cursor_column() const;
+ void set_cursor_position(int p_line, int p_column);
+ void set_multiline_mode(bool p_state);
+ bool is_past_cursor() const;
+ static String get_token_name(Token::Type p_token_type);
- Vector<StringName> identifiers;
- Vector<Variant> constants;
- VMap<uint32_t, uint32_t> lines;
- Vector<uint32_t> tokens;
- Variant nil;
- int token;
-
-public:
- Error set_code_buffer(const Vector<uint8_t> &p_buffer);
- static Vector<uint8_t> parse_code_string(const String &p_code);
- virtual Token get_token(int p_offset = 0) const;
- virtual StringName get_token_identifier(int p_offset = 0) const;
- virtual GDScriptFunctions::Function get_token_built_in_func(int p_offset = 0) const;
- virtual Variant::Type get_token_type(int p_offset = 0) const;
- virtual int get_token_line(int p_offset = 0) const;
- virtual int get_token_column(int p_offset = 0) const;
- virtual int get_token_line_indent(int p_offset = 0) const;
- virtual int get_token_line_tab_indent(int p_offset = 0) const { return 0; }
- virtual const Variant &get_token_constant(int p_offset = 0) const;
- virtual String get_token_error(int p_offset = 0) const;
- virtual void advance(int p_amount = 1);
-#ifdef DEBUG_ENABLED
- virtual const Vector<Pair<int, String>> &get_warning_skips() const {
- static Vector<Pair<int, String>> v;
- return v;
- }
- virtual const Set<String> &get_warning_global_skips() const {
- static Set<String> s;
- return s;
- }
- virtual bool is_ignoring_warnings() const { return true; }
-#endif // DEBUG_ENABLED
- GDScriptTokenizerBuffer();
+ GDScriptTokenizer();
};
-#endif // GDSCRIPT_TOKENIZER_H
+#endif
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
new file mode 100644
index 0000000000..105facd9d0
--- /dev/null
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -0,0 +1,210 @@
+/*************************************************************************/
+/* gdscript_warning.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "gdscript_warning.h"
+
+#include "core/variant.h"
+
+#ifdef DEBUG_ENABLED
+
+String GDScriptWarning::get_message() const {
+#define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String());
+
+ switch (code) {
+ case UNASSIGNED_VARIABLE_OP_ASSIGN: {
+ CHECK_SYMBOLS(1);
+ return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value.";
+ } break;
+ case UNASSIGNED_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The variable '" + symbols[0] + "' was used but never assigned a value.";
+ } break;
+ case UNUSED_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'";
+ } break;
+ case UNUSED_LOCAL_CONSTANT: {
+ CHECK_SYMBOLS(1);
+ return "The local constant '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'";
+ } break;
+ case SHADOWED_VARIABLE: {
+ CHECK_SYMBOLS(4);
+ return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s.)", symbols[0], symbols[1], symbols[2], symbols[3]);
+ } break;
+ case SHADOWED_VARIABLE_BASE_CLASS: {
+ CHECK_SYMBOLS(4);
+ return vformat(R"(The local %s "%s" is shadowing an already-declared %s at the base class "%s".)", symbols[0], symbols[1], symbols[2], symbols[3]);
+ } break;
+ case UNUSED_PRIVATE_CLASS_VARIABLE: {
+ CHECK_SYMBOLS(1);
+ return "The class variable '" + symbols[0] + "' is declared but never used in the script.";
+ } break;
+ case UNUSED_PARAMETER: {
+ CHECK_SYMBOLS(2);
+ return "The parameter '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'";
+ } break;
+ case UNREACHABLE_CODE: {
+ CHECK_SYMBOLS(1);
+ return "Unreachable code (statement after return) in function '" + symbols[0] + "()'.";
+ } break;
+ case UNREACHABLE_PATTERN: {
+ return "Unreachable pattern (pattern after wildcard or bind).";
+ } break;
+ case STANDALONE_EXPRESSION: {
+ return "Standalone expression (the line has no effect).";
+ } break;
+ case VOID_ASSIGNMENT: {
+ CHECK_SYMBOLS(1);
+ return "Assignment operation, but the function '" + symbols[0] + "()' returns void.";
+ } break;
+ case NARROWING_CONVERSION: {
+ return "Narrowing conversion (float is converted to int and loses precision).";
+ } break;
+ case INCOMPATIBLE_TERNARY: {
+ return "Values of the ternary conditional are not mutually compatible.";
+ } break;
+ case UNUSED_SIGNAL: {
+ CHECK_SYMBOLS(1);
+ return "The signal '" + symbols[0] + "' is declared but never emitted.";
+ } break;
+ case RETURN_VALUE_DISCARDED: {
+ CHECK_SYMBOLS(1);
+ return "The function '" + symbols[0] + "()' returns a value, but this value is never used.";
+ } break;
+ case PROPERTY_USED_AS_FUNCTION: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?";
+ } break;
+ case CONSTANT_USED_AS_FUNCTION: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?";
+ } break;
+ case FUNCTION_USED_AS_PROPERTY: {
+ CHECK_SYMBOLS(2);
+ return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?";
+ } break;
+ case INTEGER_DIVISION: {
+ return "Integer division, decimal part will be discarded.";
+ } break;
+ case UNSAFE_PROPERTY_ACCESS: {
+ CHECK_SYMBOLS(2);
+ return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
+ } break;
+ case UNSAFE_METHOD_ACCESS: {
+ CHECK_SYMBOLS(2);
+ return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype).";
+ } break;
+ case UNSAFE_CAST: {
+ CHECK_SYMBOLS(1);
+ return "The value is cast to '" + symbols[0] + "' but has an unknown type.";
+ } break;
+ case UNSAFE_CALL_ARGUMENT: {
+ CHECK_SYMBOLS(4);
+ return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided";
+ } break;
+ case DEPRECATED_KEYWORD: {
+ CHECK_SYMBOLS(2);
+ return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'.";
+ } break;
+ case STANDALONE_TERNARY: {
+ return "Standalone ternary conditional operator: the return value is being discarded.";
+ }
+ case ASSERT_ALWAYS_TRUE: {
+ return "Assert statement is redundant because the expression is always true.";
+ }
+ case ASSERT_ALWAYS_FALSE: {
+ return "Assert statement will raise an error because the expression is always false.";
+ }
+ case REDUNDANT_AWAIT: {
+ return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)";
+ }
+ case WARNING_MAX:
+ break; // Can't happen, but silences warning
+ }
+ ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + ".");
+
+#undef CHECK_SYMBOLS
+}
+
+String GDScriptWarning::get_name() const {
+ return get_name_from_code(code);
+}
+
+String GDScriptWarning::get_name_from_code(Code p_code) {
+ ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String());
+
+ static const char *names[] = {
+ "UNASSIGNED_VARIABLE",
+ "UNASSIGNED_VARIABLE_OP_ASSIGN",
+ "UNUSED_VARIABLE",
+ "UNUSED_LOCAL_CONSTANT",
+ "SHADOWED_VARIABLE",
+ "SHADOWED_VARIABLE_BASE_CLASS",
+ "UNUSED_PRIVATE_CLASS_VARIABLE",
+ "UNUSED_PARAMETER",
+ "UNREACHABLE_CODE",
+ "UNREACHABLE_PATTERN",
+ "STANDALONE_EXPRESSION",
+ "VOID_ASSIGNMENT",
+ "NARROWING_CONVERSION",
+ "INCOMPATIBLE_TERNARY",
+ "UNUSED_SIGNAL",
+ "RETURN_VALUE_DISCARDED",
+ "PROPERTY_USED_AS_FUNCTION",
+ "CONSTANT_USED_AS_FUNCTION",
+ "FUNCTION_USED_AS_PROPERTY",
+ "INTEGER_DIVISION",
+ "UNSAFE_PROPERTY_ACCESS",
+ "UNSAFE_METHOD_ACCESS",
+ "UNSAFE_CAST",
+ "UNSAFE_CALL_ARGUMENT",
+ "DEPRECATED_KEYWORD",
+ "STANDALONE_TERNARY",
+ "ASSERT_ALWAYS_TRUE",
+ "ASSERT_ALWAYS_FALSE",
+ "REDUNDANT_AWAIT",
+ };
+
+ static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
+
+ return names[(int)p_code];
+}
+
+GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) {
+ for (int i = 0; i < WARNING_MAX; i++) {
+ if (get_name_from_code((Code)i) == p_name) {
+ return (Code)i;
+ }
+ }
+
+ ERR_FAIL_V_MSG(WARNING_MAX, "Invalid GDScript warning name: " + p_name);
+}
+
+#endif // DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
new file mode 100644
index 0000000000..e183d6f302
--- /dev/null
+++ b/modules/gdscript/gdscript_warning.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* gdscript_warning.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 GDSCRIPT_WARNINGS
+#define GDSCRIPT_WARNINGS
+
+#ifdef DEBUG_ENABLED
+
+#include "core/ustring.h"
+#include "core/vector.h"
+
+class GDScriptWarning {
+public:
+ enum Code {
+ UNASSIGNED_VARIABLE, // Variable used but never assigned.
+ UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc).
+ UNUSED_VARIABLE, // Local variable is declared but never used.
+ UNUSED_LOCAL_CONSTANT, // Local constant is declared but never used.
+ SHADOWED_VARIABLE, // Variable name shadowed by other variable in same class.
+ SHADOWED_VARIABLE_BASE_CLASS, // Variable name shadowed by other variable in some base class.
+ UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("_" prefix) but never used in the file.
+ UNUSED_PARAMETER, // Function parameter is never used.
+ UNREACHABLE_CODE, // Code after a return statement.
+ UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).
+ STANDALONE_EXPRESSION, // Expression not assigned to a variable.
+ VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable.
+ NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
+ INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible.
+ UNUSED_SIGNAL, // Signal is defined but never emitted.
+ RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used.
+ PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name.
+ CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name.
+ FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name.
+ INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded.
+ UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes).
+ UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes).
+ UNSAFE_CAST, // Cast used in an unknown type.
+ UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument.
+ DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
+ STANDALONE_TERNARY, // Return value of ternary expression is discarded.
+ ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
+ ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false.
+ REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
+ WARNING_MAX,
+ };
+
+ Code code = WARNING_MAX;
+ int start_line = -1, end_line = -1;
+ int leftmost_column = -1, rightmost_column = -1;
+ Vector<String> symbols;
+
+ String get_name() const;
+ String get_message() const;
+ static String get_name_from_code(Code p_code);
+ static Code get_code_from_name(const String &p_name);
+};
+
+#endif // DEBUG_ENABLED
+
+#endif // GDSCRIPT_WARNINGS
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index f87e8687e5..668dfd4835 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -29,7 +29,9 @@
/*************************************************************************/
#include "gdscript_extend_parser.h"
+
#include "../gdscript.h"
+#include "../gdscript_analyzer.h"
#include "core/io/json.h"
#include "gdscript_language_protocol.h"
#include "gdscript_workspace.h"
@@ -37,15 +39,17 @@
void ExtendGDScriptParser::update_diagnostics() {
diagnostics.clear();
- if (has_error()) {
+ const List<ParserError> &errors = get_errors();
+ for (const List<ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
+ const ParserError &error = E->get();
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Error;
- diagnostic.message = get_error();
+ diagnostic.message = error.message;
diagnostic.source = "gdscript";
diagnostic.code = -1;
lsp::Range range;
lsp::Position pos;
- int line = LINE_NUMBER_TO_INDEX(get_error_line());
+ int line = LINE_NUMBER_TO_INDEX(error.line);
const String &line_text = get_lines()[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
@@ -61,12 +65,12 @@ void ExtendGDScriptParser::update_diagnostics() {
const GDScriptWarning &warning = E->get();
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Warning;
- diagnostic.message = warning.get_message();
+ diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message();
diagnostic.source = "gdscript";
diagnostic.code = warning.code;
lsp::Range range;
lsp::Position pos;
- int line = LINE_NUMBER_TO_INDEX(warning.line);
+ int line = LINE_NUMBER_TO_INDEX(warning.start_line);
const String &line_text = get_lines()[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
@@ -81,7 +85,7 @@ void ExtendGDScriptParser::update_diagnostics() {
void ExtendGDScriptParser::update_symbols() {
members.clear();
- const GDScriptParser::Node *head = get_parse_tree();
+ const GDScriptParser::Node *head = get_tree();
if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
parse_class_symbol(gdclass, class_symbol);
@@ -105,15 +109,15 @@ void ExtendGDScriptParser::update_symbols() {
void ExtendGDScriptParser::update_document_links(const String &p_code) {
document_links.clear();
- GDScriptTokenizerText tokenizer;
+ GDScriptTokenizer tokenizer;
FileAccessRef fs = FileAccess::create(FileAccess::ACCESS_RESOURCES);
- tokenizer.set_code(p_code);
+ tokenizer.set_source_code(p_code);
while (true) {
- GDScriptTokenizerText::Token token = tokenizer.get_token();
- if (token == GDScriptTokenizer::TK_EOF || token == GDScriptTokenizer::TK_ERROR) {
+ GDScriptTokenizer::Token token = tokenizer.scan();
+ if (token.type == GDScriptTokenizer::Token::TK_EOF) {
break;
- } else if (token == GDScriptTokenizer::TK_CONSTANT) {
- const Variant &const_val = tokenizer.get_token_constant();
+ } else if (token.type == GDScriptTokenizer::Token::LITERAL) {
+ const Variant &const_val = token.literal;
if (const_val.get_type() == Variant::STRING) {
String path = const_val;
bool exists = fs->file_exists(path);
@@ -125,15 +129,14 @@ void ExtendGDScriptParser::update_document_links(const String &p_code) {
String value = const_val;
lsp::DocumentLink link;
link.target = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(path);
- link.range.start.line = LINE_NUMBER_TO_INDEX(tokenizer.get_token_line());
- link.range.end.line = link.range.start.line;
- link.range.end.character = LINE_NUMBER_TO_INDEX(tokenizer.get_token_column());
- link.range.start.character = link.range.end.character - value.length();
+ link.range.start.line = LINE_NUMBER_TO_INDEX(token.start_line);
+ link.range.end.line = LINE_NUMBER_TO_INDEX(token.end_line);
+ link.range.start.character = LINE_NUMBER_TO_INDEX(token.start_column);
+ link.range.end.character = LINE_NUMBER_TO_INDEX(token.end_column);
document_links.push_back(link);
}
}
}
- tokenizer.advance();
}
}
@@ -143,219 +146,238 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
r_symbol.uri = uri;
r_symbol.script_path = path;
r_symbol.children.clear();
- r_symbol.name = p_class->name;
+ r_symbol.name = p_class->identifier != nullptr ? String(p_class->identifier->name) : String();
if (r_symbol.name.empty()) {
r_symbol.name = path.get_file();
}
r_symbol.kind = lsp::SymbolKind::Class;
r_symbol.deprecated = false;
- r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_class->line);
- r_symbol.range.start.character = p_class->column;
+ r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_class->start_line);
+ r_symbol.range.start.character = LINE_NUMBER_TO_INDEX(p_class->start_column);
r_symbol.range.end.line = LINE_NUMBER_TO_INDEX(p_class->end_line);
r_symbol.selectionRange.start.line = r_symbol.range.start.line;
r_symbol.detail = "class " + r_symbol.name;
bool is_root_class = &r_symbol == &class_symbol;
- r_symbol.documentation = parse_documentation(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class);
-
- for (int i = 0; i < p_class->variables.size(); ++i) {
- const GDScriptParser::ClassNode::Member &m = p_class->variables[i];
-
- lsp::DocumentSymbol symbol;
- symbol.name = m.identifier;
- symbol.kind = lsp::SymbolKind::Variable;
- symbol.deprecated = false;
- const int line = LINE_NUMBER_TO_INDEX(m.line);
- symbol.range.start.line = line;
- symbol.range.start.character = lines[line].length() - lines[line].strip_edges(true, false).length();
- symbol.range.end.line = line;
- symbol.range.end.character = lines[line].length();
- symbol.selectionRange.start.line = symbol.range.start.line;
- if (m._export.type != Variant::NIL) {
- symbol.detail += "export ";
- }
- symbol.detail += "var " + m.identifier;
- if (m.data_type.kind != GDScriptParser::DataType::UNRESOLVED) {
- symbol.detail += ": " + m.data_type.to_string();
- }
- if (m.default_value.get_type() != Variant::NIL) {
- symbol.detail += " = " + JSON::print(m.default_value);
- }
-
- symbol.documentation = parse_documentation(line);
- symbol.uri = uri;
- symbol.script_path = path;
-
- r_symbol.children.push_back(symbol);
- }
-
- for (int i = 0; i < p_class->_signals.size(); ++i) {
- const GDScriptParser::ClassNode::Signal &signal = p_class->_signals[i];
-
- lsp::DocumentSymbol symbol;
- symbol.name = signal.name;
- symbol.kind = lsp::SymbolKind::Event;
- symbol.deprecated = false;
- const int line = LINE_NUMBER_TO_INDEX(signal.line);
- symbol.range.start.line = line;
- symbol.range.start.character = lines[line].length() - lines[line].strip_edges(true, false).length();
- symbol.range.end.line = symbol.range.start.line;
- symbol.range.end.character = lines[line].length();
- symbol.selectionRange.start.line = symbol.range.start.line;
- symbol.documentation = parse_documentation(line);
- symbol.uri = uri;
- symbol.script_path = path;
- symbol.detail = "signal " + signal.name + "(";
- for (int j = 0; j < signal.arguments.size(); j++) {
- if (j > 0) {
- symbol.detail += ", ";
- }
- symbol.detail += signal.arguments[j];
- }
- symbol.detail += ")";
-
- r_symbol.children.push_back(symbol);
- }
+ r_symbol.documentation = parse_documentation(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->start_line), is_root_class);
+
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const ClassNode::Member &m = p_class->members[i];
+
+ switch (m.type) {
+ case ClassNode::Member::VARIABLE: {
+ lsp::DocumentSymbol symbol;
+ symbol.name = m.variable->identifier->name;
+ symbol.kind = lsp::SymbolKind::Variable;
+ symbol.deprecated = false;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.variable->end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.variable->end_column);
+ symbol.selectionRange.start.line = symbol.range.start.line;
+ if (m.variable->exported) {
+ symbol.detail += "@export ";
+ }
+ symbol.detail += "var " + m.variable->identifier->name;
+ if (m.get_datatype().is_hard_type()) {
+ symbol.detail += ": " + m.get_datatype().to_string();
+ }
+ if (m.variable->initializer != nullptr && m.variable->initializer->is_constant) {
+ symbol.detail += " = " + JSON::print(m.variable->initializer->reduced_value);
+ }
- for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
- lsp::DocumentSymbol symbol;
- const GDScriptParser::ClassNode::Constant &c = E->value();
- const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression);
- ERR_FAIL_COND(!node);
- symbol.name = E->key();
- symbol.kind = lsp::SymbolKind::Constant;
- symbol.deprecated = false;
- const int line = LINE_NUMBER_TO_INDEX(E->get().expression->line);
- symbol.range.start.line = line;
- symbol.range.start.character = E->get().expression->column;
- symbol.range.end.line = symbol.range.start.line;
- symbol.range.end.character = lines[line].length();
- symbol.selectionRange.start.line = symbol.range.start.line;
- symbol.documentation = parse_documentation(line);
- symbol.uri = uri;
- symbol.script_path = path;
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.variable->start_line));
+ symbol.uri = uri;
+ symbol.script_path = path;
+
+ r_symbol.children.push_back(symbol);
+ } break;
+ case ClassNode::Member::CONSTANT: {
+ lsp::DocumentSymbol symbol;
+
+ symbol.name = m.constant->identifier->name;
+ symbol.kind = lsp::SymbolKind::Constant;
+ symbol.deprecated = false;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.constant->start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.constant->start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.constant->end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.constant->start_column);
+ symbol.selectionRange.start.line = LINE_NUMBER_TO_INDEX(m.constant->start_line);
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.constant->start_line));
+ symbol.uri = uri;
+ symbol.script_path = path;
+
+ symbol.detail = "const " + symbol.name;
+ if (m.constant->get_datatype().is_hard_type()) {
+ symbol.detail += ": " + m.constant->get_datatype().to_string();
+ }
- symbol.detail = "const " + symbol.name;
- if (c.type.kind != GDScriptParser::DataType::UNRESOLVED) {
- symbol.detail += ": " + c.type.to_string();
- }
+ const Variant &default_value = m.constant->initializer->reduced_value;
+ String value_text;
+ if (default_value.get_type() == Variant::OBJECT) {
+ RES res = default_value;
+ if (res.is_valid() && !res->get_path().empty()) {
+ value_text = "preload(\"" + res->get_path() + "\")";
+ if (symbol.documentation.empty()) {
+ if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
+ symbol.documentation = S->get()->class_symbol.documentation;
+ }
+ }
+ } else {
+ value_text = JSON::print(default_value);
+ }
+ } else {
+ value_text = JSON::print(default_value);
+ }
+ if (!value_text.empty()) {
+ symbol.detail += " = " + value_text;
+ }
- String value_text;
- if (node->value.get_type() == Variant::OBJECT) {
- RES res = node->value;
- if (res.is_valid() && !res->get_path().empty()) {
- value_text = "preload(\"" + res->get_path() + "\")";
- if (symbol.documentation.empty()) {
- if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
- symbol.documentation = S->get()->class_symbol.documentation;
+ r_symbol.children.push_back(symbol);
+ } break;
+ case ClassNode::Member::ENUM_VALUE: {
+ lsp::DocumentSymbol symbol;
+
+ symbol.name = m.enum_value.identifier->name;
+ symbol.kind = lsp::SymbolKind::EnumMember;
+ symbol.deprecated = false;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.enum_value.line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.enum_value.leftmost_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.enum_value.line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.enum_value.rightmost_column);
+ symbol.selectionRange.start.line = LINE_NUMBER_TO_INDEX(m.enum_value.line);
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.enum_value.line));
+ symbol.uri = uri;
+ symbol.script_path = path;
+
+ symbol.detail = symbol.name + " = " + itos(m.enum_value.value);
+
+ r_symbol.children.push_back(symbol);
+ } break;
+ case ClassNode::Member::SIGNAL: {
+ lsp::DocumentSymbol symbol;
+ symbol.name = m.signal->identifier->name;
+ symbol.kind = lsp::SymbolKind::Event;
+ symbol.deprecated = false;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.signal->start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.signal->start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.signal->end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.signal->end_column);
+ symbol.selectionRange.start.line = symbol.range.start.line;
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.signal->start_line));
+ symbol.uri = uri;
+ symbol.script_path = path;
+ symbol.detail = "signal " + String(m.signal->identifier->name) + "(";
+ for (int j = 0; j < m.signal->parameters.size(); j++) {
+ if (j > 0) {
+ symbol.detail += ", ";
}
+ symbol.detail += m.signal->parameters[i]->identifier->name;
}
- } else {
- value_text = JSON::print(node->value);
- }
- } else {
- value_text = JSON::print(node->value);
- }
- if (!value_text.empty()) {
- symbol.detail += " = " + value_text;
+ symbol.detail += ")";
+
+ r_symbol.children.push_back(symbol);
+ } break;
+ case ClassNode::Member::ENUM: {
+ lsp::DocumentSymbol symbol;
+ symbol.kind = lsp::SymbolKind::Enum;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.m_enum->start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.m_enum->start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.m_enum->end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.m_enum->end_column);
+ symbol.selectionRange.start.line = symbol.range.start.line;
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.m_enum->start_line));
+ symbol.uri = uri;
+ symbol.script_path = path;
+
+ symbol.detail = "enum " + String(m.m_enum->identifier->name) + "{";
+ for (int j = 0; j < m.m_enum->values.size(); j++) {
+ if (j > 0) {
+ symbol.detail += ", ";
+ }
+ symbol.detail += String(m.m_enum->values[j].identifier->name) + " = " + itos(m.m_enum->values[j].value);
+ }
+ symbol.detail += "}";
+ r_symbol.children.push_back(symbol);
+ } break;
+ case ClassNode::Member::FUNCTION: {
+ lsp::DocumentSymbol symbol;
+ parse_function_symbol(m.function, symbol);
+ r_symbol.children.push_back(symbol);
+ } break;
+ case ClassNode::Member::CLASS: {
+ lsp::DocumentSymbol symbol;
+ parse_class_symbol(m.m_class, symbol);
+ r_symbol.children.push_back(symbol);
+ } break;
+ case ClassNode::Member::UNDEFINED:
+ break; // Unreachable.
}
-
- r_symbol.children.push_back(symbol);
- }
-
- for (int i = 0; i < p_class->functions.size(); ++i) {
- const GDScriptParser::FunctionNode *func = p_class->functions[i];
- lsp::DocumentSymbol symbol;
- parse_function_symbol(func, symbol);
- r_symbol.children.push_back(symbol);
- }
-
- for (int i = 0; i < p_class->static_functions.size(); ++i) {
- const GDScriptParser::FunctionNode *func = p_class->static_functions[i];
- lsp::DocumentSymbol symbol;
- parse_function_symbol(func, symbol);
- r_symbol.children.push_back(symbol);
- }
-
- for (int i = 0; i < p_class->subclasses.size(); ++i) {
- const GDScriptParser::ClassNode *subclass = p_class->subclasses[i];
- lsp::DocumentSymbol symbol;
- parse_class_symbol(subclass, symbol);
- r_symbol.children.push_back(symbol);
}
}
void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol) {
const String uri = get_uri();
- r_symbol.name = p_func->name;
+ r_symbol.name = p_func->identifier->name;
r_symbol.kind = lsp::SymbolKind::Function;
- r_symbol.detail = "func " + p_func->name + "(";
+ r_symbol.detail = "func " + String(p_func->identifier->name) + "(";
r_symbol.deprecated = false;
- const int line = LINE_NUMBER_TO_INDEX(p_func->line);
- r_symbol.range.start.line = line;
- r_symbol.range.start.character = p_func->column;
- r_symbol.range.end.line = MAX(p_func->body->end_line - 2, r_symbol.range.start.line);
- r_symbol.range.end.character = lines[r_symbol.range.end.line].length();
+ r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_func->start_line);
+ r_symbol.range.start.character = LINE_NUMBER_TO_INDEX(p_func->start_column);
+ r_symbol.range.end.line = LINE_NUMBER_TO_INDEX(p_func->start_line);
+ r_symbol.range.end.character = LINE_NUMBER_TO_INDEX(p_func->end_column);
r_symbol.selectionRange.start.line = r_symbol.range.start.line;
- r_symbol.documentation = parse_documentation(line);
+ r_symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(p_func->start_line));
r_symbol.uri = uri;
r_symbol.script_path = path;
- String arguments;
- for (int i = 0; i < p_func->arguments.size(); i++) {
+ String parameters;
+ for (int i = 0; i < p_func->parameters.size(); i++) {
+ const ParameterNode *parameter = p_func->parameters[i];
lsp::DocumentSymbol symbol;
symbol.kind = lsp::SymbolKind::Variable;
- symbol.name = p_func->arguments[i];
- symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_func->body->line);
- symbol.range.start.character = p_func->body->column;
- symbol.range.end = symbol.range.start;
+ symbol.name = parameter->identifier->name;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(parameter->start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(parameter->start_line);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(parameter->end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(parameter->end_column);
symbol.uri = uri;
symbol.script_path = path;
r_symbol.children.push_back(symbol);
if (i > 0) {
- arguments += ", ";
+ parameters += ", ";
}
- arguments += String(p_func->arguments[i]);
- if (p_func->argument_types[i].kind != GDScriptParser::DataType::UNRESOLVED) {
- arguments += ": " + p_func->argument_types[i].to_string();
+ parameters += String(parameter->identifier->name);
+ if (parameter->get_datatype().is_hard_type()) {
+ parameters += ": " + parameter->get_datatype().to_string();
}
- int default_value_idx = i - (p_func->arguments.size() - p_func->default_values.size());
- if (default_value_idx >= 0) {
- const GDScriptParser::ConstantNode *const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(p_func->default_values[default_value_idx]);
- if (const_node == nullptr) {
- const GDScriptParser::OperatorNode *operator_node = dynamic_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[default_value_idx]);
- if (operator_node) {
- const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(operator_node->next);
- }
- }
-
- if (const_node) {
- String value = JSON::print(const_node->value);
- arguments += " = " + value;
- }
+ if (parameter->default_value != nullptr) {
+ String value = JSON::print(parameter->default_value->reduced_value);
+ parameters += " = " + value;
}
}
- r_symbol.detail += arguments + ")";
- if (p_func->return_type.kind != GDScriptParser::DataType::UNRESOLVED) {
- r_symbol.detail += " -> " + p_func->return_type.to_string();
+ r_symbol.detail += parameters + ")";
+ if (p_func->get_datatype().is_hard_type()) {
+ r_symbol.detail += " -> " + p_func->get_datatype().to_string();
}
- for (const Map<StringName, LocalVarNode *>::Element *E = p_func->body->variables.front(); E; E = E->next()) {
+ for (int i = 0; i < p_func->body->locals.size(); i++) {
+ const SuiteNode::Local &local = p_func->body->locals[i];
lsp::DocumentSymbol symbol;
- const GDScriptParser::LocalVarNode *var = E->value();
- symbol.name = E->key();
- symbol.kind = lsp::SymbolKind::Variable;
- symbol.range.start.line = LINE_NUMBER_TO_INDEX(E->get()->line);
- symbol.range.start.character = E->get()->column;
- symbol.range.end.line = symbol.range.start.line;
- symbol.range.end.character = lines[symbol.range.end.line].length();
+ symbol.name = local.name;
+ symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column);
symbol.uri = uri;
symbol.script_path = path;
- symbol.detail = "var " + symbol.name;
- if (var->datatype.kind != GDScriptParser::DataType::UNRESOLVED) {
- symbol.detail += ": " + var->datatype.to_string();
+ symbol.detail = SuiteNode::Local::CONSTANT ? "const " : "var ";
+ symbol.detail += symbol.name;
+ if (local.get_datatype().is_hard_type()) {
+ symbol.detail += ": " + local.get_datatype().to_string();
}
- symbol.documentation = parse_documentation(line);
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line));
r_symbol.children.push_back(symbol);
}
}
@@ -469,7 +491,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
int start_pos = p_position.character;
for (int c = p_position.character; c >= 0; c--) {
start_pos = c;
- CharType ch = line[c];
+ char32_t ch = line[c];
bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
if (!valid_char) {
break;
@@ -478,7 +500,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
int end_pos = p_position.character;
for (int c = p_position.character; c < line.length(); c++) {
- CharType ch = line[c];
+ char32_t ch = line[c];
bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
if (!valid_char) {
break;
@@ -530,7 +552,7 @@ Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_positi
}
while (c >= 0) {
- const CharType &character = line[c];
+ const char32_t &character = line[c];
if (character == ')') {
++bracket_stack;
} else if (character == '(') {
@@ -623,34 +645,24 @@ const Array &ExtendGDScriptParser::get_member_completions() {
Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::FunctionNode *p_func) const {
Dictionary func;
ERR_FAIL_NULL_V(p_func, func);
- func["name"] = p_func->name;
- func["return_type"] = p_func->return_type.to_string();
+ func["name"] = p_func->identifier->name;
+ func["return_type"] = p_func->get_datatype().to_string();
func["rpc_mode"] = p_func->rpc_mode;
- Array arguments;
- for (int i = 0; i < p_func->arguments.size(); i++) {
+ Array parameters;
+ for (int i = 0; i < p_func->parameters.size(); i++) {
Dictionary arg;
- arg["name"] = p_func->arguments[i];
- arg["type"] = p_func->argument_types[i].to_string();
- int default_value_idx = i - (p_func->arguments.size() - p_func->default_values.size());
- if (default_value_idx >= 0) {
- const GDScriptParser::ConstantNode *const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(p_func->default_values[default_value_idx]);
- if (const_node == nullptr) {
- const GDScriptParser::OperatorNode *operator_node = dynamic_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[default_value_idx]);
- if (operator_node) {
- const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(operator_node->next);
- }
- }
- if (const_node) {
- arg["default_value"] = const_node->value;
- }
+ arg["name"] = p_func->parameters[i]->identifier->name;
+ arg["type"] = p_func->parameters[i]->get_datatype().to_string();
+ if (p_func->parameters[i]->default_value != nullptr) {
+ arg["default_value"] = p_func->parameters[i]->default_value->reduced_value;
}
- arguments.push_back(arg);
+ parameters.push_back(arg);
}
- if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_func->line))) {
+ if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_func->start_line))) {
func["signature"] = symbol->detail;
func["description"] = symbol->documentation;
}
- func["arguments"] = arguments;
+ func["arguments"] = parameters;
return func;
}
@@ -659,91 +671,117 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
ERR_FAIL_NULL_V(p_class, class_api);
- class_api["name"] = String(p_class->name);
+ class_api["name"] = p_class->identifier != nullptr ? String(p_class->identifier->name) : String();
class_api["path"] = path;
Array extends_class;
- for (int i = 0; i < p_class->extends_class.size(); i++) {
- extends_class.append(String(p_class->extends_class[i]));
+ for (int i = 0; i < p_class->extends.size(); i++) {
+ extends_class.append(String(p_class->extends[i]));
}
class_api["extends_class"] = extends_class;
- class_api["extends_file"] = String(p_class->extends_file);
+ class_api["extends_file"] = String(p_class->extends_path);
class_api["icon"] = String(p_class->icon_path);
- if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_class->line))) {
+ if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_class->start_line))) {
class_api["signature"] = symbol->detail;
class_api["description"] = symbol->documentation;
}
- Array subclasses;
- for (int i = 0; i < p_class->subclasses.size(); i++) {
- subclasses.push_back(dump_class_api(p_class->subclasses[i]));
- }
- class_api["sub_classes"] = subclasses;
-
+ Array nested_classes;
Array constants;
- for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
- const GDScriptParser::ClassNode::Constant &c = E->value();
- const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression);
- ERR_FAIL_COND_V(!node, class_api);
-
- Dictionary api;
- api["name"] = E->key();
- api["value"] = node->value;
- api["data_type"] = node->datatype.to_string();
- if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(node->line))) {
- api["signature"] = symbol->detail;
- api["description"] = symbol->documentation;
- }
- constants.push_back(api);
- }
- class_api["constants"] = constants;
-
Array members;
- for (int i = 0; i < p_class->variables.size(); ++i) {
- const GDScriptParser::ClassNode::Member &m = p_class->variables[i];
- Dictionary api;
- api["name"] = m.identifier;
- api["data_type"] = m.data_type.to_string();
- api["default_value"] = m.default_value;
- api["setter"] = String(m.setter);
- api["getter"] = String(m.getter);
- api["export"] = m._export.type != Variant::NIL;
- if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line))) {
- api["signature"] = symbol->detail;
- api["description"] = symbol->documentation;
- }
- members.push_back(api);
- }
- class_api["members"] = members;
-
Array signals;
- for (int i = 0; i < p_class->_signals.size(); ++i) {
- const GDScriptParser::ClassNode::Signal &signal = p_class->_signals[i];
- Dictionary api;
- api["name"] = signal.name;
- Array args;
- for (int j = 0; j < signal.arguments.size(); j++) {
- args.append(signal.arguments[j]);
- }
- api["arguments"] = args;
- if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(signal.line))) {
- api["signature"] = symbol->detail;
- api["description"] = symbol->documentation;
+ Array methods;
+ Array static_functions;
+
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const ClassNode::Member &m = p_class->members[i];
+ switch (m.type) {
+ case ClassNode::Member::CLASS:
+ nested_classes.push_back(dump_class_api(m.m_class));
+ break;
+ case ClassNode::Member::CONSTANT: {
+ Dictionary api;
+ api["name"] = m.constant->identifier->name;
+ api["value"] = m.constant->initializer->reduced_value;
+ api["data_type"] = m.constant->get_datatype().to_string();
+ if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.constant->start_line))) {
+ api["signature"] = symbol->detail;
+ api["description"] = symbol->documentation;
+ }
+ constants.push_back(api);
+ } break;
+ case ClassNode::Member::ENUM_VALUE: {
+ Dictionary api;
+ api["name"] = m.enum_value.identifier->name;
+ api["value"] = m.enum_value.value;
+ api["data_type"] = m.get_datatype().to_string();
+ if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.enum_value.line))) {
+ api["signature"] = symbol->detail;
+ api["description"] = symbol->documentation;
+ }
+ constants.push_back(api);
+ } break;
+ case ClassNode::Member::ENUM: {
+ Dictionary enum_dict;
+ for (int j = 0; j < m.m_enum->values.size(); i++) {
+ enum_dict[m.m_enum->values[i].identifier->name] = m.m_enum->values[i].value;
+ }
+
+ Dictionary api;
+ api["name"] = m.m_enum->identifier->name;
+ api["value"] = enum_dict;
+ api["data_type"] = m.get_datatype().to_string();
+ if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.m_enum->start_line))) {
+ api["signature"] = symbol->detail;
+ api["description"] = symbol->documentation;
+ }
+ constants.push_back(api);
+ } break;
+ case ClassNode::Member::VARIABLE: {
+ Dictionary api;
+ api["name"] = m.variable->identifier->name;
+ api["data_type"] = m.variable->get_datatype().to_string();
+ api["default_value"] = m.variable->initializer != nullptr ? m.variable->initializer->reduced_value : Variant();
+ api["setter"] = m.variable->setter ? ("@" + String(m.variable->identifier->name) + "_setter") : (m.variable->setter_pointer != nullptr ? String(m.variable->setter_pointer->name) : String());
+ api["getter"] = m.variable->getter ? ("@" + String(m.variable->identifier->name) + "_getter") : (m.variable->getter_pointer != nullptr ? String(m.variable->getter_pointer->name) : String());
+ api["export"] = m.variable->exported;
+ if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.variable->start_line))) {
+ api["signature"] = symbol->detail;
+ api["description"] = symbol->documentation;
+ }
+ members.push_back(api);
+ } break;
+ case ClassNode::Member::SIGNAL: {
+ Dictionary api;
+ api["name"] = m.signal->identifier->name;
+ Array pars;
+ for (int j = 0; j < m.signal->parameters.size(); j++) {
+ pars.append(String(m.signal->parameters[i]->identifier->name));
+ }
+ api["arguments"] = pars;
+ if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.signal->start_line))) {
+ api["signature"] = symbol->detail;
+ api["description"] = symbol->documentation;
+ }
+ signals.push_back(api);
+ } break;
+ case ClassNode::Member::FUNCTION: {
+ if (m.function->is_static) {
+ static_functions.append(dump_function_api(m.function));
+ } else {
+ methods.append(dump_function_api(m.function));
+ }
+ } break;
+ case ClassNode::Member::UNDEFINED:
+ break; // Unreachable.
}
- signals.push_back(api);
}
- class_api["signals"] = signals;
- Array methods;
- for (int i = 0; i < p_class->functions.size(); ++i) {
- methods.append(dump_function_api(p_class->functions[i]));
- }
+ class_api["sub_classes"] = nested_classes;
+ class_api["constants"] = constants;
+ class_api["members"] = members;
+ class_api["signals"] = signals;
class_api["methods"] = methods;
-
- Array static_functions;
- for (int i = 0; i < p_class->static_functions.size(); ++i) {
- static_functions.append(dump_function_api(p_class->static_functions[i]));
- }
class_api["static_functions"] = static_functions;
return class_api;
@@ -751,7 +789,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
Dictionary ExtendGDScriptParser::generate_api() const {
Dictionary api;
- const GDScriptParser::Node *head = get_parse_tree();
+ const GDScriptParser::Node *head = get_tree();
if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
api = dump_class_api(gdclass);
}
@@ -762,7 +800,11 @@ Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
path = p_path;
lines = p_code.split("\n");
- Error err = GDScriptParser::parse(p_code, p_path.get_base_dir(), false, p_path, false, nullptr, false);
+ Error err = GDScriptParser::parse(p_code, p_path, false);
+ if (err == OK) {
+ GDScriptAnalyzer analyzer(this);
+ err = analyzer.analyze();
+ }
update_diagnostics();
update_symbols();
update_document_links(p_code);
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 35bf4287b8..2a67d2ff4f 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "gdscript_language_protocol.h"
+
#include "core/io/json.h"
#include "core/os/copymem.h"
#include "core/project_settings.h"
@@ -161,7 +162,7 @@ void GDScriptLanguageProtocol::_bind_methods() {
ClassDB::bind_method(D_METHOD("initialized", "params"), &GDScriptLanguageProtocol::initialized);
ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected);
ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected);
- ClassDB::bind_method(D_METHOD("notify_client", "p_method", "p_params"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("notify_client", "method", "params"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled);
ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document);
ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace);
@@ -187,8 +188,10 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
Dictionary params;
params["path"] = workspace->root;
- Dictionary request = make_notification("gdscrip_client/changeWorkspace", params);
+ Dictionary request = make_notification("gdscript_client/changeWorkspace", params);
+ ERR_FAIL_COND_V_MSG(!clients.has(latest_client_id), ret.to_json(),
+ vformat("GDScriptLanguageProtocol: Can't initialize invalid peer '%d'.", latest_client_id));
Ref<LSPeer> peer = clients.get(latest_client_id);
if (peer != nullptr) {
String msg = JSON::print(request);
@@ -268,8 +271,11 @@ void GDScriptLanguageProtocol::stop() {
void GDScriptLanguageProtocol::notify_client(const String &p_method, const Variant &p_params, int p_client_id) {
if (p_client_id == -1) {
+ ERR_FAIL_COND_MSG(latest_client_id == -1,
+ "GDScript LSP: Can't notify client as none was connected.");
p_client_id = latest_client_id;
}
+ ERR_FAIL_COND(!clients.has(p_client_id));
Ref<LSPeer> peer = clients.get(p_client_id);
ERR_FAIL_COND(peer == nullptr);
@@ -290,13 +296,10 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const {
GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
server.instance();
singleton = this;
- _initialized = false;
workspace.instance();
text_document.instance();
set_scope("textDocument", text_document.ptr());
set_scope("completionItem", text_document.ptr());
set_scope("workspace", workspace.ptr());
workspace->root = ProjectSettings::get_singleton()->get_resource_path();
- latest_client_id = 0;
- next_client_id = 0;
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index d929fd255d..cf5242e8c5 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -70,8 +70,8 @@ private:
HashMap<int, Ref<LSPeer>> clients;
Ref<TCP_Server> server;
- int latest_client_id;
- int next_client_id;
+ int latest_client_id = 0;
+ int next_client_id = 0;
Ref<GDScriptTextDocument> text_document;
Ref<GDScriptWorkspace> workspace;
@@ -82,7 +82,7 @@ private:
String process_message(const String &p_text);
String format_output(const String &p_text);
- bool _initialized;
+ bool _initialized = false;
protected:
static void _bind_methods();
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index d53914814f..3387d262f8 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "gdscript_language_server.h"
+
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "editor/editor_log.h"
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 778cb4d254..c6fe3169dc 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "gdscript_text_document.h"
+
#include "../gdscript.h"
#include "core/os/os.h"
#include "editor/editor_settings.h"
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 9285d88157..776193e37c 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "gdscript_workspace.h"
+
#include "../gdscript.h"
#include "../gdscript_parser.h"
#include "core/project_settings.h"
@@ -41,12 +42,12 @@
void GDScriptWorkspace::_bind_methods() {
ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
- ClassDB::bind_method(D_METHOD("parse_script", "p_path", "p_content"), &GDScriptWorkspace::parse_script);
- ClassDB::bind_method(D_METHOD("parse_local_script", "p_path"), &GDScriptWorkspace::parse_local_script);
- ClassDB::bind_method(D_METHOD("get_file_path", "p_uri"), &GDScriptWorkspace::get_file_path);
- ClassDB::bind_method(D_METHOD("get_file_uri", "p_path"), &GDScriptWorkspace::get_file_uri);
- ClassDB::bind_method(D_METHOD("publish_diagnostics", "p_path"), &GDScriptWorkspace::publish_diagnostics);
- ClassDB::bind_method(D_METHOD("generate_script_api", "p_path"), &GDScriptWorkspace::generate_script_api);
+ ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
+ ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script);
+ ClassDB::bind_method(D_METHOD("get_file_path", "uri"), &GDScriptWorkspace::get_file_path);
+ ClassDB::bind_method(D_METHOD("get_file_uri", "path"), &GDScriptWorkspace::get_file_uri);
+ ClassDB::bind_method(D_METHOD("publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics);
+ ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);
}
void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
@@ -117,7 +118,7 @@ void GDScriptWorkspace::reload_all_workspace_scripts() {
Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(path);
String err_msg = "Failed parse script " + path;
if (S) {
- err_msg += "\n" + S->get()->get_error();
+ err_msg += "\n" + S->get()->get_errors()[0].message;
}
ERR_CONTINUE_MSG(err != OK, err_msg);
}
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 44a0076107..cf27a1578c 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -486,7 +486,7 @@ struct TextDocumentSyncOptions {
* If present save notifications are sent to the server. If omitted the notification should not be
* sent.
*/
- SaveOptions save;
+ bool save = false;
Dictionary to_json() {
Dictionary dict;
@@ -494,7 +494,7 @@ struct TextDocumentSyncOptions {
dict["willSave"] = willSave;
dict["openClose"] = openClose;
dict["change"] = change;
- dict["save"] = save.to_json();
+ dict["save"] = save;
return dict;
}
};
@@ -1656,7 +1656,7 @@ struct ServerCapabilities {
_FORCE_INLINE_ Dictionary to_json() {
Dictionary dict;
- dict["textDocumentSync"] = (int)textDocumentSync.change;
+ dict["textDocumentSync"] = textDocumentSync.to_json();
dict["completionProvider"] = completionProvider.to_json();
signatureHelpProvider.triggerCharacters.push_back(",");
signatureHelpProvider.triggerCharacters.push_back("(");
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 0625123530..da4cbe34c7 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -35,29 +35,41 @@
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "gdscript.h"
+#include "gdscript_analyzer.h"
+#include "gdscript_cache.h"
#include "gdscript_tokenizer.h"
+#ifdef TESTS_ENABLED
+#include "tests/test_gdscript.h"
+#include "tests/test_macros.h"
+#endif
+
GDScriptLanguage *script_language_gd = nullptr;
Ref<ResourceFormatLoaderGDScript> resource_loader_gd;
Ref<ResourceFormatSaverGDScript> resource_saver_gd;
+GDScriptCache *gdscript_cache = nullptr;
#ifdef TOOLS_ENABLED
#include "editor/editor_export.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/editor_translation_parser.h"
#include "editor/gdscript_highlighter.h"
+#include "editor/gdscript_translation_parser_plugin.h"
#ifndef GDSCRIPT_NO_LSP
#include "core/engine.h"
#include "language_server/gdscript_language_server.h"
#endif // !GDSCRIPT_NO_LSP
+Ref<GDScriptEditorTranslationParserPlugin> gdscript_translation_parser_plugin;
+
class EditorExportGDScript : public EditorExportPlugin {
GDCLASS(EditorExportGDScript, EditorExportPlugin);
public:
- virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
+ virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features) override {
int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED;
String script_key;
@@ -72,64 +84,8 @@ public:
return;
}
- Vector<uint8_t> file = FileAccess::get_file_as_array(p_path);
- if (file.empty()) {
- return;
- }
-
- String txt;
- txt.parse_utf8((const char *)file.ptr(), file.size());
- file = GDScriptTokenizerBuffer::parse_code_string(txt);
-
- if (!file.empty()) {
- if (script_mode == EditorExportPreset::MODE_SCRIPT_ENCRYPTED) {
- String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("script.gde");
- FileAccess *fa = FileAccess::open(tmp_path, FileAccess::WRITE);
-
- Vector<uint8_t> key;
- key.resize(32);
- for (int i = 0; i < 32; i++) {
- int v = 0;
- if (i * 2 < script_key.length()) {
- CharType ct = script_key[i * 2];
- if (ct >= '0' && ct <= '9') {
- ct = ct - '0';
- } else if (ct >= 'a' && ct <= 'f') {
- ct = 10 + ct - 'a';
- }
- v |= ct << 4;
- }
-
- if (i * 2 + 1 < script_key.length()) {
- CharType ct = script_key[i * 2 + 1];
- if (ct >= '0' && ct <= '9') {
- ct = ct - '0';
- } else if (ct >= 'a' && ct <= 'f') {
- ct = 10 + ct - 'a';
- }
- v |= ct;
- }
- key.write[i] = v;
- }
- FileAccessEncrypted *fae = memnew(FileAccessEncrypted);
- Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_WRITE_AES256);
-
- if (err == OK) {
- fae->store_buffer(file.ptr(), file.size());
- }
-
- memdelete(fae);
-
- file = FileAccess::get_file_as_array(tmp_path);
- add_file(p_path.get_basename() + ".gde", file, true);
-
- // Clean up temporary file.
- DirAccess::remove_file_or_error(tmp_path);
-
- } else {
- add_file(p_path.get_basename() + ".gdc", file, true);
- }
- }
+ // TODO: Readd compiled GDScript on export.
+ return;
}
};
@@ -138,6 +94,12 @@ static void _editor_init() {
gd_export.instance();
EditorExport::get_singleton()->add_export_plugin(gd_export);
+#ifdef TOOLS_ENABLED
+ Ref<GDScriptSyntaxHighlighter> gdscript_syntax_highlighter;
+ gdscript_syntax_highlighter.instance();
+ ScriptEditor::get_singleton()->register_syntax_highlighter(gdscript_syntax_highlighter);
+#endif
+
#ifndef GDSCRIPT_NO_LSP
register_lsp_types();
GDScriptLanguageServer *lsp_plugin = memnew(GDScriptLanguageServer);
@@ -161,15 +123,23 @@ void register_gdscript_types() {
resource_saver_gd.instance();
ResourceSaver::add_resource_format_saver(resource_saver_gd);
+ gdscript_cache = memnew(GDScriptCache);
+
#ifdef TOOLS_ENABLED
- ScriptEditor::register_create_syntax_highlighter_function(GDScriptSyntaxHighlighter::create);
EditorNode::add_init_callback(_editor_init);
+
+ gdscript_translation_parser_plugin.instance();
+ EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
#endif // TOOLS_ENABLED
}
void unregister_gdscript_types() {
ScriptServer::unregister_language(script_language_gd);
+ if (gdscript_cache) {
+ memdelete(gdscript_cache);
+ }
+
if (script_language_gd) {
memdelete(script_language_gd);
}
@@ -179,4 +149,35 @@ void unregister_gdscript_types() {
ResourceSaver::remove_resource_format_saver(resource_saver_gd);
resource_saver_gd.unref();
+
+#ifdef TOOLS_ENABLED
+ EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
+ gdscript_translation_parser_plugin.unref();
+#endif // TOOLS_ENABLED
+
+ GDScriptParser::cleanup();
+ GDScriptAnalyzer::cleanup();
+}
+
+#ifdef TESTS_ENABLED
+void test_tokenizer() {
+ TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER);
+}
+
+void test_parser() {
+ TestGDScript::test(TestGDScript::TestType::TEST_PARSER);
+}
+
+void test_compiler() {
+ TestGDScript::test(TestGDScript::TestType::TEST_COMPILER);
}
+
+void test_bytecode() {
+ TestGDScript::test(TestGDScript::TestType::TEST_BYTECODE);
+}
+
+REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer);
+REGISTER_TEST_COMMAND("gdscript-parser", &test_parser);
+REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler);
+REGISTER_TEST_COMMAND("gdscript-bytecode", &test_bytecode);
+#endif
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
new file mode 100644
index 0000000000..68d9984b43
--- /dev/null
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -0,0 +1,231 @@
+/*************************************************************************/
+/* test_gdscript.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "test_gdscript.h"
+
+#include "core/os/file_access.h"
+#include "core/os/main_loop.h"
+#include "core/os/os.h"
+#include "core/string_builder.h"
+
+#include "modules/gdscript/gdscript_analyzer.h"
+#include "modules/gdscript/gdscript_compiler.h"
+#include "modules/gdscript/gdscript_parser.h"
+#include "modules/gdscript/gdscript_tokenizer.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif
+
+namespace TestGDScript {
+
+static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) {
+ GDScriptTokenizer tokenizer;
+ tokenizer.set_source_code(p_code);
+
+ int tab_size = 4;
+#ifdef TOOLS_ENABLED
+ if (EditorSettings::get_singleton()) {
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ }
+#endif // TOOLS_ENABLED
+ String tab = String(" ").repeat(tab_size);
+
+ GDScriptTokenizer::Token current = tokenizer.scan();
+ while (current.type != GDScriptTokenizer::Token::TK_EOF) {
+ StringBuilder token;
+ token += " --> "; // Padding for line number.
+
+ for (int l = current.start_line; l <= current.end_line; l++) {
+ print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab));
+ }
+
+ {
+ // Print carets to point at the token.
+ StringBuilder pointer;
+ pointer += " "; // Padding for line number.
+ int rightmost_column = current.rightmost_column;
+ if (current.end_line > current.start_line) {
+ rightmost_column--; // Don't point to the newline as a column.
+ }
+ for (int col = 1; col < rightmost_column; col++) {
+ if (col < current.leftmost_column) {
+ pointer += " ";
+ } else {
+ pointer += "^";
+ }
+ }
+ print_line(pointer.as_string());
+ }
+
+ token += current.get_name();
+
+ if (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::LITERAL || current.type == GDScriptTokenizer::Token::IDENTIFIER || current.type == GDScriptTokenizer::Token::ANNOTATION) {
+ token += "(";
+ token += Variant::get_type_name(current.literal.get_type());
+ token += ") ";
+ token += current.literal;
+ }
+
+ print_line(token.as_string());
+
+ print_line("-------------------------------------------------------");
+
+ current = tokenizer.scan();
+ }
+
+ print_line(current.get_name()); // Should be EOF
+}
+
+static void test_parser(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
+ GDScriptParser parser;
+ Error err = parser.parse(p_code, p_script_path, false);
+
+ if (err != OK) {
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
+ const GDScriptParser::ParserError &error = E->get();
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ }
+
+ GDScriptParser::TreePrinter printer;
+
+ printer.print_tree(parser);
+}
+
+static void test_compiler(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
+ GDScriptParser parser;
+ Error err = parser.parse(p_code, p_script_path, false);
+
+ if (err != OK) {
+ print_line("Error in parser:");
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
+ const GDScriptParser::ParserError &error = E->get();
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ return;
+ }
+
+ GDScriptAnalyzer analyzer(&parser);
+ err = analyzer.analyze();
+
+ if (err != OK) {
+ print_line("Error in analyzer:");
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
+ const GDScriptParser::ParserError &error = E->get();
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ return;
+ }
+
+ GDScriptCompiler compiler;
+ Ref<GDScript> script;
+ script.instance();
+ script->set_path(p_script_path);
+
+ err = compiler.compile(&parser, script.ptr(), false);
+
+ if (err) {
+ print_line("Error in compiler:");
+ print_line(vformat("%02d:%02d: %s", compiler.get_error_line(), compiler.get_error_column(), compiler.get_error()));
+ return;
+ }
+
+ for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
+ const GDScriptFunction *func = E->value();
+
+ String signature = "Disassembling " + func->get_name().operator String() + "(";
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ if (i > 0) {
+ signature += ", ";
+ }
+ signature += func->get_argument_name(i);
+ }
+ print_line(signature + ")");
+
+ func->disassemble(p_lines);
+ print_line("");
+ print_line("");
+ }
+}
+
+void test(TestType p_type) {
+ List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
+
+ if (cmdlargs.empty()) {
+ return;
+ }
+
+ String test = cmdlargs.back()->get();
+ if (!test.ends_with(".gd")) {
+ print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test);
+ return;
+ }
+
+ FileAccessRef fa = FileAccess::open(test, FileAccess::READ);
+ ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test);
+
+ Vector<uint8_t> buf;
+ int flen = fa->get_len();
+ buf.resize(fa->get_len() + 1);
+ fa->get_buffer(buf.ptrw(), flen);
+ buf.write[flen] = 0;
+
+ String code;
+ code.parse_utf8((const char *)&buf[0]);
+
+ Vector<String> lines;
+ int last = 0;
+ for (int i = 0; i <= code.length(); i++) {
+ if (code[i] == '\n' || code[i] == 0) {
+ lines.push_back(code.substr(last, i - last));
+ last = i + 1;
+ }
+ }
+
+ switch (p_type) {
+ case TEST_TOKENIZER:
+ test_tokenizer(code, lines);
+ break;
+ case TEST_PARSER:
+ test_parser(code, test, lines);
+ break;
+ case TEST_COMPILER:
+ test_compiler(code, test, lines);
+ break;
+ case TEST_BYTECODE:
+ print_line("Not implemented.");
+ }
+}
+
+} // namespace TestGDScript
diff --git a/modules/mono/editor/csharp_project.h b/modules/gdscript/tests/test_gdscript.h
index 515b8d3d62..5aa962dcf8 100644
--- a/modules/mono/editor/csharp_project.h
+++ b/modules/gdscript/tests/test_gdscript.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* csharp_project.h */
+/* test_gdscript.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,20 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CSHARP_PROJECT_H
-#define CSHARP_PROJECT_H
+#ifndef TEST_GDSCRIPT_H
+#define TEST_GDSCRIPT_H
-#include "core/ustring.h"
+namespace TestGDScript {
-namespace CSharpProject {
+enum TestType {
+ TEST_TOKENIZER,
+ TEST_PARSER,
+ TEST_COMPILER,
+ TEST_BYTECODE,
+};
-void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
+void test(TestType p_type);
-} // namespace CSharpProject
+} // namespace TestGDScript
-#endif // CSHARP_PROJECT_H
+#endif // TEST_GDSCRIPT_H
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index a213069e19..57fbc5bfc0 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -10,7 +10,7 @@
Internally, a GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link>
+ <link title="Using gridmaps">https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link>
</tutorials>
<methods>
<method name="clear">
@@ -43,27 +43,19 @@
<method name="get_cell_item" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
- </argument>
- <argument index="2" name="z" type="int">
+ <argument index="0" name="position" type="Vector3i">
</argument>
<description>
- The [MeshLibrary] item index located at the grid-based X, Y and Z coordinates. If the cell is empty, [constant INVALID_CELL_ITEM] will be returned.
+ The [MeshLibrary] item index located at the given grid coordinates. If the cell is empty, [constant INVALID_CELL_ITEM] will be returned.
</description>
</method>
<method name="get_cell_item_orientation" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
- </argument>
- <argument index="2" name="z" type="int">
+ <argument index="0" name="position" type="Vector3i">
</argument>
<description>
- The orientation of the cell at the grid-based X, Y and Z coordinates. -1 is returned if the cell is empty.
+ The orientation of the cell at the given grid coordinates. [code]-1[/code] is returned if the cell is empty.
</description>
</method>
<method name="get_collision_layer_bit" qualifiers="const">
@@ -111,11 +103,7 @@
<method name="map_to_world" qualifiers="const">
<return type="Vector3">
</return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
- </argument>
- <argument index="2" name="z" type="int">
+ <argument index="0" name="map_position" type="Vector3i">
</argument>
<description>
Returns the position of a grid cell in the GridMap's local coordinate space.
@@ -132,18 +120,14 @@
<method name="set_cell_item">
<return type="void">
</return>
- <argument index="0" name="x" type="int">
- </argument>
- <argument index="1" name="y" type="int">
+ <argument index="0" name="position" type="Vector3i">
</argument>
- <argument index="2" name="z" type="int">
+ <argument index="1" name="item" type="int">
</argument>
- <argument index="3" name="item" type="int">
- </argument>
- <argument index="4" name="orientation" type="int" default="0">
+ <argument index="2" name="orientation" type="int" default="0">
</argument>
<description>
- Sets the mesh index for the cell referenced by its grid-based X, Y and Z coordinates.
+ Sets the mesh index for the cell referenced by its grid coordinates.
A negative item index such as [constant INVALID_CELL_ITEM] will clear the cell.
Optionally, the item's orientation can be passed. For valid orientation values, see [method Basis.get_orthogonal_index].
</description>
@@ -185,9 +169,9 @@
</description>
</method>
<method name="world_to_map" qualifiers="const">
- <return type="Vector3">
+ <return type="Vector3i">
</return>
- <argument index="0" name="pos" type="Vector3">
+ <argument index="0" name="world_position" type="Vector3">
</argument>
<description>
Returns the coordinates of the grid cell containing the given point.
@@ -221,7 +205,7 @@
GridMaps act as static bodies, meaning they aren't affected by gravity or other forces. They only affect other physics bodies that collide with them.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The physics layers this GridMap detects collisions in.
+ The physics layers this GridMap detects collisions in. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library">
The assigned [MeshLibrary].
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 2975a97bfe..ccf1e49693 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -244,26 +244,26 @@ bool GridMap::get_center_z() const {
return center_z;
}
-void GridMap::set_cell_item(int p_x, int p_y, int p_z, int p_item, int p_rot) {
+void GridMap::set_cell_item(const Vector3i &p_position, int p_item, int p_rot) {
if (baked_meshes.size() && !recreating_octants) {
//if you set a cell item, baked meshes go good bye
clear_baked_meshes();
_recreate_octant_data();
}
- ERR_FAIL_INDEX(ABS(p_x), 1 << 20);
- ERR_FAIL_INDEX(ABS(p_y), 1 << 20);
- ERR_FAIL_INDEX(ABS(p_z), 1 << 20);
+ ERR_FAIL_INDEX(ABS(p_position.x), 1 << 20);
+ ERR_FAIL_INDEX(ABS(p_position.y), 1 << 20);
+ ERR_FAIL_INDEX(ABS(p_position.z), 1 << 20);
IndexKey key;
- key.x = p_x;
- key.y = p_y;
- key.z = p_z;
+ key.x = p_position.x;
+ key.y = p_position.y;
+ key.z = p_position.z;
OctantKey ok;
- ok.x = p_x / octant_size;
- ok.y = p_y / octant_size;
- ok.z = p_z / octant_size;
+ ok.x = p_position.x / octant_size;
+ ok.y = p_position.y / octant_size;
+ ok.z = p_position.z / octant_size;
if (p_item < 0) {
//erase
@@ -318,15 +318,15 @@ void GridMap::set_cell_item(int p_x, int p_y, int p_z, int p_item, int p_rot) {
cell_map[key] = c;
}
-int GridMap::get_cell_item(int p_x, int p_y, int p_z) const {
- ERR_FAIL_INDEX_V(ABS(p_x), 1 << 20, INVALID_CELL_ITEM);
- ERR_FAIL_INDEX_V(ABS(p_y), 1 << 20, INVALID_CELL_ITEM);
- ERR_FAIL_INDEX_V(ABS(p_z), 1 << 20, INVALID_CELL_ITEM);
+int GridMap::get_cell_item(const Vector3i &p_position) const {
+ ERR_FAIL_INDEX_V(ABS(p_position.x), 1 << 20, INVALID_CELL_ITEM);
+ ERR_FAIL_INDEX_V(ABS(p_position.y), 1 << 20, INVALID_CELL_ITEM);
+ ERR_FAIL_INDEX_V(ABS(p_position.z), 1 << 20, INVALID_CELL_ITEM);
IndexKey key;
- key.x = p_x;
- key.y = p_y;
- key.z = p_z;
+ key.x = p_position.x;
+ key.y = p_position.y;
+ key.z = p_position.z;
if (!cell_map.has(key)) {
return INVALID_CELL_ITEM;
@@ -334,15 +334,15 @@ int GridMap::get_cell_item(int p_x, int p_y, int p_z) const {
return cell_map[key].item;
}
-int GridMap::get_cell_item_orientation(int p_x, int p_y, int p_z) const {
- ERR_FAIL_INDEX_V(ABS(p_x), 1 << 20, -1);
- ERR_FAIL_INDEX_V(ABS(p_y), 1 << 20, -1);
- ERR_FAIL_INDEX_V(ABS(p_z), 1 << 20, -1);
+int GridMap::get_cell_item_orientation(const Vector3i &p_position) const {
+ ERR_FAIL_INDEX_V(ABS(p_position.x), 1 << 20, -1);
+ ERR_FAIL_INDEX_V(ABS(p_position.y), 1 << 20, -1);
+ ERR_FAIL_INDEX_V(ABS(p_position.z), 1 << 20, -1);
IndexKey key;
- key.x = p_x;
- key.y = p_y;
- key.z = p_z;
+ key.x = p_position.x;
+ key.y = p_position.y;
+ key.z = p_position.z;
if (!cell_map.has(key)) {
return -1;
@@ -350,20 +350,20 @@ int GridMap::get_cell_item_orientation(int p_x, int p_y, int p_z) const {
return cell_map[key].rot;
}
-Vector3 GridMap::world_to_map(const Vector3 &p_world_pos) const {
- Vector3 map_pos = p_world_pos / cell_size;
- map_pos.x = floor(map_pos.x);
- map_pos.y = floor(map_pos.y);
- map_pos.z = floor(map_pos.z);
- return map_pos;
+Vector3i GridMap::world_to_map(const Vector3 &p_world_position) const {
+ Vector3 map_position = p_world_position / cell_size;
+ map_position.x = floor(map_position.x);
+ map_position.y = floor(map_position.y);
+ map_position.z = floor(map_position.z);
+ return Vector3i(map_position);
}
-Vector3 GridMap::map_to_world(int p_x, int p_y, int p_z) const {
+Vector3 GridMap::map_to_world(const Vector3i &p_map_position) const {
Vector3 offset = _get_offset();
Vector3 world_pos(
- p_x * cell_size.x + offset.x,
- p_y * cell_size.y + offset.y,
- p_z * cell_size.z + offset.z);
+ p_map_position.x * cell_size.x + offset.x,
+ p_map_position.y * cell_size.y + offset.y,
+ p_map_position.z * cell_size.z + offset.z);
return world_pos;
}
@@ -725,7 +725,7 @@ void GridMap::_recreate_octant_data() {
Map<IndexKey, Cell> cell_copy = cell_map;
_clear_internal();
for (Map<IndexKey, Cell>::Element *E = cell_copy.front(); E; E = E->next()) {
- set_cell_item(E->key().x, E->key().y, E->key().z, E->get().item, E->get().rot);
+ set_cell_item(Vector3i(E->key()), E->get().item, E->get().rot);
}
recreating_octants = false;
}
@@ -799,12 +799,12 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_octant_size", "size"), &GridMap::set_octant_size);
ClassDB::bind_method(D_METHOD("get_octant_size"), &GridMap::get_octant_size);
- ClassDB::bind_method(D_METHOD("set_cell_item", "x", "y", "z", "item", "orientation"), &GridMap::set_cell_item, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("get_cell_item", "x", "y", "z"), &GridMap::get_cell_item);
- ClassDB::bind_method(D_METHOD("get_cell_item_orientation", "x", "y", "z"), &GridMap::get_cell_item_orientation);
+ ClassDB::bind_method(D_METHOD("set_cell_item", "position", "item", "orientation"), &GridMap::set_cell_item, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_cell_item", "position"), &GridMap::get_cell_item);
+ ClassDB::bind_method(D_METHOD("get_cell_item_orientation", "position"), &GridMap::get_cell_item_orientation);
- ClassDB::bind_method(D_METHOD("world_to_map", "pos"), &GridMap::world_to_map);
- ClassDB::bind_method(D_METHOD("map_to_world", "x", "y", "z"), &GridMap::map_to_world);
+ ClassDB::bind_method(D_METHOD("world_to_map", "world_position"), &GridMap::world_to_map);
+ ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &GridMap::map_to_world);
ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback);
ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed);
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 9eb9aee7d1..ca7429ea26 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -59,6 +59,15 @@ class GridMap : public Node3D {
return key < p_key.key;
}
+ _FORCE_INLINE_ operator Vector3i() const {
+ return Vector3i(x, y, z);
+ }
+
+ IndexKey(Vector3i p_vector) {
+ x = (int16_t)p_vector.x;
+ y = (int16_t)p_vector.y;
+ z = (int16_t)p_vector.z;
+ }
IndexKey() { key = 0; }
};
@@ -234,12 +243,12 @@ public:
void set_center_z(bool p_enable);
bool get_center_z() const;
- void set_cell_item(int p_x, int p_y, int p_z, int p_item, int p_rot = 0);
- int get_cell_item(int p_x, int p_y, int p_z) const;
- int get_cell_item_orientation(int p_x, int p_y, int p_z) const;
+ void set_cell_item(const Vector3i &p_position, int p_item, int p_rot = 0);
+ int get_cell_item(const Vector3i &p_position) const;
+ int get_cell_item_orientation(const Vector3i &p_position) const;
- Vector3 world_to_map(const Vector3 &p_world_pos) const;
- Vector3 map_to_world(int p_x, int p_y, int p_z) const;
+ Vector3i world_to_map(const Vector3 &p_world_position) const;
+ Vector3 map_to_world(const Vector3i &p_map_position) const;
void set_clip(bool p_enabled, bool p_clip_above = true, int p_floor = 0, Vector3::Axis p_axis = Vector3::AXIS_X);
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 2bae43510a..0e6ec7f520 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -35,7 +35,6 @@
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
-#include "core/math/geometry.h"
#include "core/os/keyboard.h"
#include "scene/main/window.h"
@@ -292,15 +291,15 @@ void GridMapEditor::_update_selection_transform() {
} else {
Vector3 scale = (selection.end - selection.begin + Vector3(1, 1, 1));
scale[edit_axis] = 1.0;
- Vector3 pos = selection.begin;
- pos[edit_axis] = edit_floor[edit_axis];
+ Vector3 position = selection.begin;
+ position[edit_axis] = edit_floor[edit_axis];
scale *= node->get_cell_size();
- pos *= node->get_cell_size();
+ position *= node->get_cell_size();
Transform xf2;
xf2.basis.scale(scale);
- xf2.origin = pos;
+ xf2.origin = position;
RenderingServer::get_singleton()->instance_set_transform(selection_level_instance[i], xf2);
}
@@ -415,11 +414,11 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b
}
if (input_action == INPUT_PASTE) {
- paste_indicator.current = Vector3(cell[0], cell[1], cell[2]);
+ paste_indicator.current = Vector3i(cell[0], cell[1], cell[2]);
_update_paste_indicator();
} else if (input_action == INPUT_SELECT) {
- selection.current = Vector3(cell[0], cell[1], cell[2]);
+ selection.current = Vector3i(cell[0], cell[1], cell[2]);
if (p_click) {
selection.click = selection.current;
}
@@ -428,7 +427,7 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b
return true;
} else if (input_action == INPUT_PICK) {
- int item = node->get_cell_item(cell[0], cell[1], cell[2]);
+ int item = node->get_cell_item(Vector3i(cell[0], cell[1], cell[2]));
if (item >= 0) {
selected_palette = item;
mesh_library_palette->set_current(item);
@@ -439,23 +438,23 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b
}
if (input_action == INPUT_PAINT) {
SetItem si;
- si.pos = Vector3(cell[0], cell[1], cell[2]);
+ si.position = Vector3i(cell[0], cell[1], cell[2]);
si.new_value = selected_palette;
si.new_orientation = cursor_rot;
- si.old_value = node->get_cell_item(cell[0], cell[1], cell[2]);
- si.old_orientation = node->get_cell_item_orientation(cell[0], cell[1], cell[2]);
+ si.old_value = node->get_cell_item(Vector3i(cell[0], cell[1], cell[2]));
+ si.old_orientation = node->get_cell_item_orientation(Vector3i(cell[0], cell[1], cell[2]));
set_items.push_back(si);
- node->set_cell_item(cell[0], cell[1], cell[2], selected_palette, cursor_rot);
+ node->set_cell_item(Vector3i(cell[0], cell[1], cell[2]), selected_palette, cursor_rot);
return true;
} else if (input_action == INPUT_ERASE) {
SetItem si;
- si.pos = Vector3(cell[0], cell[1], cell[2]);
+ si.position = Vector3i(cell[0], cell[1], cell[2]);
si.new_value = -1;
si.new_orientation = 0;
- si.old_value = node->get_cell_item(cell[0], cell[1], cell[2]);
- si.old_orientation = node->get_cell_item_orientation(cell[0], cell[1], cell[2]);
+ si.old_value = node->get_cell_item(Vector3i(cell[0], cell[1], cell[2]));
+ si.old_orientation = node->get_cell_item_orientation(Vector3i(cell[0], cell[1], cell[2]));
set_items.push_back(si);
- node->set_cell_item(cell[0], cell[1], cell[2], -1);
+ node->set_cell_item(Vector3i(cell[0], cell[1], cell[2]), -1);
return true;
}
@@ -471,8 +470,9 @@ void GridMapEditor::_delete_selection() {
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
for (int k = selection.begin.z; k <= selection.end.z; k++) {
- undo_redo->add_do_method(node, "set_cell_item", i, j, k, GridMap::INVALID_CELL_ITEM);
- undo_redo->add_undo_method(node, "set_cell_item", i, j, k, node->get_cell_item(i, j, k), node->get_cell_item_orientation(i, j, k));
+ Vector3i selected = Vector3i(i, j, k);
+ undo_redo->add_do_method(node, "set_cell_item", selected, GridMap::INVALID_CELL_ITEM);
+ undo_redo->add_undo_method(node, "set_cell_item", selected, node->get_cell_item(selected), node->get_cell_item_orientation(selected));
}
}
}
@@ -490,8 +490,9 @@ void GridMapEditor::_fill_selection() {
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
for (int k = selection.begin.z; k <= selection.end.z; k++) {
- undo_redo->add_do_method(node, "set_cell_item", i, j, k, selected_palette, cursor_rot);
- undo_redo->add_undo_method(node, "set_cell_item", i, j, k, node->get_cell_item(i, j, k), node->get_cell_item_orientation(i, j, k));
+ Vector3i selected = Vector3i(i, j, k);
+ undo_redo->add_do_method(node, "set_cell_item", selected, selected_palette, cursor_rot);
+ undo_redo->add_undo_method(node, "set_cell_item", selected, node->get_cell_item(selected), node->get_cell_item_orientation(selected));
}
}
}
@@ -516,7 +517,8 @@ void GridMapEditor::_set_clipboard_data() {
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
for (int k = selection.begin.z; k <= selection.end.z; k++) {
- int itm = node->get_cell_item(i, j, k);
+ Vector3i selected = Vector3i(i, j, k);
+ int itm = node->get_cell_item(selected);
if (itm == GridMap::INVALID_CELL_ITEM) {
continue;
}
@@ -525,8 +527,8 @@ void GridMapEditor::_set_clipboard_data() {
ClipboardItem item;
item.cell_item = itm;
- item.grid_offset = Vector3(i, j, k) - selection.begin;
- item.orientation = node->get_cell_item_orientation(i, j, k);
+ item.grid_offset = Vector3(selected) - selection.begin;
+ item.orientation = node->get_cell_item_orientation(selected);
item.instance = RenderingServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world_3d()->get_scenario());
clipboard_items.push_back(item);
@@ -584,14 +586,14 @@ void GridMapEditor::_do_paste() {
for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
ClipboardItem &item = E->get();
- Vector3 pos = rot.xform(item.grid_offset) + paste_indicator.begin + ofs;
+ Vector3 position = rot.xform(item.grid_offset) + paste_indicator.begin + ofs;
Basis orm;
orm.set_orthogonal_index(item.orientation);
orm = rot * orm;
- undo_redo->add_do_method(node, "set_cell_item", pos.x, pos.y, pos.z, item.cell_item, orm.get_orthogonal_index());
- undo_redo->add_undo_method(node, "set_cell_item", pos.x, pos.y, pos.z, node->get_cell_item(pos.x, pos.y, pos.z), node->get_cell_item_orientation(pos.x, pos.y, pos.z));
+ undo_redo->add_do_method(node, "set_cell_item", position, item.cell_item, orm.get_orthogonal_index());
+ undo_redo->add_undo_method(node, "set_cell_item", position, node->get_cell_item(position), node->get_cell_item_orientation(position));
}
if (reselect) {
@@ -668,11 +670,11 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
undo_redo->create_action(TTR("GridMap Paint"));
for (List<SetItem>::Element *E = set_items.front(); E; E = E->next()) {
const SetItem &si = E->get();
- undo_redo->add_do_method(node, "set_cell_item", si.pos.x, si.pos.y, si.pos.z, si.new_value, si.new_orientation);
+ undo_redo->add_do_method(node, "set_cell_item", si.position, si.new_value, si.new_orientation);
}
for (List<SetItem>::Element *E = set_items.back(); E; E = E->prev()) {
const SetItem &si = E->get();
- undo_redo->add_undo_method(node, "set_cell_item", si.pos.x, si.pos.y, si.pos.z, si.old_value, si.old_orientation);
+ undo_redo->add_undo_method(node, "set_cell_item", si.position, si.old_value, si.old_orientation);
}
undo_redo->commit_action();
@@ -1242,14 +1244,16 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
search_box->connect("text_changed", callable_mp(this, &GridMapEditor::_text_changed));
search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input));
- mode_thumbnail = memnew(ToolButton);
+ mode_thumbnail = memnew(Button);
+ mode_thumbnail->set_flat(true);
mode_thumbnail->set_toggle_mode(true);
mode_thumbnail->set_pressed(true);
mode_thumbnail->set_icon(p_editor->get_gui_base()->get_theme_icon("FileThumbnail", "EditorIcons"));
hb->add_child(mode_thumbnail);
mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_THUMBNAIL));
- mode_list = memnew(ToolButton);
+ mode_list = memnew(Button);
+ mode_list->set_flat(true);
mode_list->set_toggle_mode(true);
mode_list->set_pressed(false);
mode_list->set_icon(p_editor->get_gui_base()->get_theme_icon("FileList", "EditorIcons"));
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 19eea18965..7e136ff9bb 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -33,7 +33,6 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
-#include "editor/pane_drag.h"
#include "grid_map.h"
class Node3DEditorPlugin;
@@ -74,8 +73,8 @@ class GridMapEditor : public VBoxContainer {
MenuButton *options;
SpinBox *floor;
double accumulated_floor_delta;
- ToolButton *mode_thumbnail;
- ToolButton *mode_list;
+ Button *mode_thumbnail;
+ Button *mode_list;
LineEdit *search_box;
HSlider *size_slider;
HBoxContainer *spatial_editor_hb;
@@ -85,7 +84,7 @@ class GridMapEditor : public VBoxContainer {
Label *spin_box_label;
struct SetItem {
- Vector3 pos;
+ Vector3i position;
int new_value;
int new_orientation;
int old_value;
@@ -256,12 +255,12 @@ protected:
void _notification(int p_what);
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
- virtual String get_name() const { return "GridMap"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_object);
- virtual bool handles(Object *p_object) const;
- virtual void make_visible(bool p_visible);
+ virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
+ virtual String get_name() const override { return "GridMap"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
GridMapEditorPlugin(EditorNode *p_node);
~GridMapEditorPlugin();
diff --git a/modules/jsonrpc/jsonrpc.cpp b/modules/jsonrpc/jsonrpc.cpp
index 6ab5f2d9b3..320da182f8 100644
--- a/modules/jsonrpc/jsonrpc.cpp
+++ b/modules/jsonrpc/jsonrpc.cpp
@@ -98,6 +98,10 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem
if (p_action.get_type() == Variant::DICTIONARY) {
Dictionary dict = p_action;
String method = dict.get("method", "");
+ if (method.begins_with("$/")) {
+ return ret;
+ }
+
Array args;
if (dict.has("params")) {
Variant params = dict.get("params", Variant());
@@ -120,7 +124,7 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem
}
if (object == nullptr || !object->has_method(method)) {
- ret = make_response_error(JSONRPC::METHOD_NOT_FOUND, "Method not found", id);
+ ret = make_response_error(JSONRPC::METHOD_NOT_FOUND, "Method not found: " + method, id);
} else {
Variant call_ret = object->callv(method, args);
if (id.get_type() != Variant::NIL) {
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index b55c73e9bc..4de523baa0 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "lightmapper_rd.h"
-#include "core/math/geometry.h"
+#include "core/math/geometry_2d.h"
#include "core/project_settings.h"
#include "lm_blendseams.glsl.gen.h"
#include "lm_compute.glsl.gen.h"
@@ -137,7 +137,7 @@ void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Ve
{
Vector3 qsize = aabb.size * 0.5; //quarter size, for fast aabb test
- if (!Geometry::triangle_box_overlap(aabb.position + qsize, qsize, p_points)) {
+ if (!Geometry3D::triangle_box_overlap(aabb.position + qsize, qsize, p_points)) {
//does not fit in child, go on
continue;
}
@@ -198,7 +198,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
int slices = 0;
while (source_sizes.size() > 0) {
- Vector<Vector3i> offsets = Geometry::partial_pack_rects(source_sizes, atlas_size);
+ Vector<Vector3i> offsets = Geometry2D::partial_pack_rects(source_sizes, atlas_size);
Vector<int> new_indices;
Vector<Vector2i> new_sources;
for (int i = 0; i < offsets.size(); i++) {
@@ -488,9 +488,9 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
}
//generate SDF for raytracing
- Vector<uint32_t> euclidean_pos = Geometry::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), false);
- Vector<uint32_t> euclidean_neg = Geometry::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), true);
- Vector<int8_t> sdf8 = Geometry::generate_sdf8(euclidean_pos, euclidean_neg);
+ Vector<uint32_t> euclidean_pos = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), false);
+ Vector<uint32_t> euclidean_neg = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), true);
+ Vector<int8_t> sdf8 = Geometry3D::generate_sdf8(euclidean_pos, euclidean_neg);
/*****************************/
/*** CREATE GPU STRUCTURES ***/
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index 6cb3ecbe9e..cd000414cf 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -235,22 +235,22 @@ class LightmapperRD : public Lightmapper {
void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform);
public:
- virtual void add_mesh(const MeshData &p_mesh);
- virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance);
- virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size);
- virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size);
- virtual void add_probe(const Vector3 &p_position);
- virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr);
-
- int get_bake_texture_count() const;
- Ref<Image> get_bake_texture(int p_index) const;
- int get_bake_mesh_count() const;
- Variant get_bake_mesh_userdata(int p_index) const;
- Rect2 get_bake_mesh_uv_scale(int p_index) const;
- int get_bake_mesh_texture_slice(int p_index) const;
- int get_bake_probe_count() const;
- Vector3 get_bake_probe_point(int p_probe) const;
- Vector<Color> get_bake_probe_sh(int p_probe) const;
+ virtual void add_mesh(const MeshData &p_mesh) override;
+ virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) override;
+ virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) override;
+ virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) override;
+ virtual void add_probe(const Vector3 &p_position) override;
+ virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr) override;
+
+ int get_bake_texture_count() const override;
+ Ref<Image> get_bake_texture(int p_index) const override;
+ int get_bake_mesh_count() const override;
+ Variant get_bake_mesh_userdata(int p_index) const override;
+ Rect2 get_bake_mesh_uv_scale(int p_index) const override;
+ int get_bake_mesh_texture_slice(int p_index) const override;
+ int get_bake_probe_count() const override;
+ Vector3 get_bake_probe_point(int p_probe) const override;
+ Vector<Color> get_bake_probe_sh(int p_probe) const override;
LightmapperRD();
};
diff --git a/modules/lightmapper_rd/lm_blendseams.glsl b/modules/lightmapper_rd/lm_blendseams.glsl
index 8a9ea91311..e47e5fcc51 100644
--- a/modules/lightmapper_rd/lm_blendseams.glsl
+++ b/modules/lightmapper_rd/lm_blendseams.glsl
@@ -1,10 +1,9 @@
-/* clang-format off */
-[versions]
+#[versions]
-lines = "#define MODE_LINES"
-triangles = "#define MODE_TRIANGLES"
+lines = "#define MODE_LINES";
+triangles = "#define MODE_TRIANGLES";
-[vertex]
+#[vertex]
#version 450
@@ -12,22 +11,20 @@ VERSION_DEFINES
#include "lm_common_inc.glsl"
- /* clang-format on */
-
- layout(push_constant, binding = 0, std430) uniform Params {
- uint base_index;
- uint slice;
- vec2 uv_offset;
- bool debug;
- float blend;
- uint pad[2];
- } params;
+layout(push_constant, binding = 0, std430) uniform Params {
+ uint base_index;
+ uint slice;
+ vec2 uv_offset;
+ bool debug;
+ float blend;
+ uint pad[2];
+}
+params;
layout(location = 0) out vec3 uv_interp;
void main() {
#ifdef MODE_TRIANGLES
-
uint triangle_idx = params.base_index + gl_VertexIndex / 3;
uint triangle_subidx = gl_VertexIndex % 3;
@@ -42,7 +39,6 @@ void main() {
uv_interp = vec3(uv, float(params.slice));
gl_Position = vec4((uv + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0);
-
#endif
#ifdef MODE_LINES
@@ -71,12 +67,10 @@ void main() {
uv_interp = vec3(src_uv, float(params.slice));
gl_Position = vec4(dst_uv * 2.0 - 1.0, 0.0001, 1.0);
- ;
#endif
}
-/* clang-format off */
-[fragment]
+#[fragment]
#version 450
@@ -84,16 +78,15 @@ VERSION_DEFINES
#include "lm_common_inc.glsl"
- /* clang-format on */
-
- layout(push_constant, binding = 0, std430) uniform Params {
- uint base_index;
- uint slice;
- vec2 uv_offset;
- bool debug;
- float blend;
- uint pad[2];
- } params;
+layout(push_constant, binding = 0, std430) uniform Params {
+ uint base_index;
+ uint slice;
+ vec2 uv_offset;
+ bool debug;
+ float blend;
+ uint pad[2];
+}
+params;
layout(location = 0) in vec3 uv_interp;
diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl
index 15946d5327..0ff455936e 100644
--- a/modules/lightmapper_rd/lm_common_inc.glsl
+++ b/modules/lightmapper_rd/lm_common_inc.glsl
@@ -11,7 +11,6 @@ struct Vertex {
layout(set = 0, binding = 1, std430) restrict readonly buffer Vertices {
Vertex data[];
}
-
vertices;
struct Triangle {
@@ -22,7 +21,6 @@ struct Triangle {
layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles {
Triangle data[];
}
-
triangles;
struct Box {
@@ -35,13 +33,11 @@ struct Box {
layout(set = 0, binding = 3, std430) restrict readonly buffer Boxes {
Box data[];
}
-
boxes;
layout(set = 0, binding = 4, std430) restrict readonly buffer GridIndices {
uint data[];
}
-
grid_indices;
#define LIGHT_TYPE_DIRECTIONAL 0
@@ -70,7 +66,6 @@ struct Light {
layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
Light data[];
}
-
lights;
struct Seam {
@@ -81,13 +76,11 @@ struct Seam {
layout(set = 0, binding = 6, std430) restrict readonly buffer Seams {
Seam data[];
}
-
seams;
layout(set = 0, binding = 7, std430) restrict readonly buffer Probes {
vec4 data[];
}
-
probe_positions;
layout(set = 0, binding = 8) uniform utexture3D grid;
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index a442016969..56976bd623 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -1,13 +1,12 @@
-/* clang-format off */
-[versions]
+#[versions]
-primary = "#define MODE_DIRECT_LIGHT"
-secondary = "#define MODE_BOUNCE_LIGHT"
-dilate = "#define MODE_DILATE"
-unocclude = "#define MODE_UNOCCLUDE"
-light_probes = "#define MODE_LIGHT_PROBES"
+primary = "#define MODE_DIRECT_LIGHT";
+secondary = "#define MODE_BOUNCE_LIGHT";
+dilate = "#define MODE_DILATE";
+unocclude = "#define MODE_UNOCCLUDE";
+light_probes = "#define MODE_LIGHT_PROBES";
-[compute]
+#[compute]
#version 450
@@ -29,14 +28,11 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "lm_common_inc.glsl"
-/* clang-format on */
-
#ifdef MODE_LIGHT_PROBES
layout(set = 1, binding = 0, std430) restrict buffer LightProbeData {
vec4 data[];
}
-
light_probes;
layout(set = 1, binding = 1) uniform texture2DArray source_light;
@@ -94,7 +90,6 @@ layout(push_constant, binding = 0, std430) uniform Params {
mat3x4 env_transform;
}
-
params;
//check it, but also return distance and barycentric coords (for uv lookup)
@@ -123,7 +118,6 @@ bool trace_ray(vec3 p_from, vec3 p_to
out float r_distance, out vec3 r_normal
#endif
) {
-
/* world coords */
vec3 rel = p_to - p_from;
@@ -149,7 +143,6 @@ bool trace_ray(vec3 p_from, vec3 p_to
while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(params.grid_size))) && iters < 1000) {
uvec2 cell_data = texelFetch(usampler3D(grid, linear_sampler), icell, 0).xy;
if (cell_data.x > 0) { //triangles here
-
bool hit = false;
#if defined(MODE_UNOCCLUDE)
bool hit_backface = false;
@@ -211,7 +204,6 @@ bool trace_ray(vec3 p_from, vec3 p_to
r_triangle = tidx;
r_barycentric = barycentric;
}
-
#endif
}
}
diff --git a/modules/lightmapper_rd/lm_raster.glsl b/modules/lightmapper_rd/lm_raster.glsl
index 36b706bcd5..6c2904192b 100644
--- a/modules/lightmapper_rd/lm_raster.glsl
+++ b/modules/lightmapper_rd/lm_raster.glsl
@@ -1,5 +1,4 @@
-/* clang-format off */
-[vertex]
+#[vertex]
#version 450
@@ -7,9 +6,7 @@ VERSION_DEFINES
#include "lm_common_inc.glsl"
- /* clang-format on */
-
- layout(location = 0) out vec3 vertex_interp;
+layout(location = 0) out vec3 vertex_interp;
layout(location = 1) out vec3 normal_interp;
layout(location = 2) out vec2 uv_interp;
layout(location = 3) out vec3 barycentric;
@@ -26,11 +23,8 @@ layout(push_constant, binding = 0, std430) uniform Params {
ivec3 grid_size;
uint pad2;
}
-
params;
-/* clang-format on */
-
void main() {
uint triangle_idx = params.base_triangle + gl_VertexIndex / 3;
uint triangle_subidx = gl_VertexIndex % 3;
@@ -56,12 +50,9 @@ void main() {
face_normal = -normalize(cross((vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.y].position), (vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.z].position)));
gl_Position = vec4((uv_interp + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0);
- ;
}
-/* clang-format off */
-
-[fragment]
+#[fragment]
#version 450
@@ -69,7 +60,6 @@ VERSION_DEFINES
#include "lm_common_inc.glsl"
-
layout(push_constant, binding = 0, std430) uniform Params {
vec2 atlas_size;
vec2 uv_offset;
@@ -79,9 +69,8 @@ layout(push_constant, binding = 0, std430) uniform Params {
float bias;
ivec3 grid_size;
uint pad2;
-} params;
-
-/* clang-format on */
+}
+params;
layout(location = 0) in vec3 vertex_interp;
layout(location = 1) in vec3 normal_interp;
@@ -100,7 +89,6 @@ void main() {
{
// smooth out vertex position by interpolating its projection in the 3 normal planes (normal plane is created by vertex pos and normal)
// because we don't want to interpolate inwards, normals found pointing inwards are pushed out.
-
vec3 pos_a = vertices.data[vertex_indices.x].position;
vec3 pos_b = vertices.data[vertex_indices.y].position;
vec3 pos_c = vertices.data[vertex_indices.z].position;
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 1f9d8c2aa3..12a982df6e 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -43,14 +43,14 @@
#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
#define PEM_END_CRT "-----END CERTIFICATE-----\n"
-#include "mbedtls/pem.h"
#include <mbedtls/debug.h>
+#include <mbedtls/pem.h>
CryptoKey *CryptoKeyMbedTLS::create() {
return memnew(CryptoKeyMbedTLS);
}
-Error CryptoKeyMbedTLS::load(String p_path) {
+Error CryptoKeyMbedTLS::load(String p_path, bool p_public_only) {
ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Key is in use");
PackedByteArray out;
@@ -59,42 +59,81 @@ Error CryptoKeyMbedTLS::load(String p_path) {
int flen = f->get_len();
out.resize(flen + 1);
- {
- uint8_t *w = out.ptrw();
- f->get_buffer(w, flen);
- w[flen] = 0; //end f string
- }
+ f->get_buffer(out.ptrw(), flen);
+ out.write[flen] = 0; // string terminator
memdelete(f);
- int ret = mbedtls_pk_parse_key(&pkey, out.ptr(), out.size(), nullptr, 0);
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_parse_public_key(&pkey, out.ptr(), out.size());
+ } else {
+ ret = mbedtls_pk_parse_key(&pkey, out.ptr(), out.size(), nullptr, 0);
+ }
// We MUST zeroize the memory for safety!
mbedtls_platform_zeroize(out.ptrw(), out.size());
- ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing private key '" + itos(ret) + "'.");
+ ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing key '" + itos(ret) + "'.");
+ public_only = p_public_only;
return OK;
}
-Error CryptoKeyMbedTLS::save(String p_path) {
+Error CryptoKeyMbedTLS::save(String p_path, bool p_public_only) {
FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot save CryptoKeyMbedTLS file '" + p_path + "'.");
unsigned char w[16000];
memset(w, 0, sizeof(w));
- int ret = mbedtls_pk_write_key_pem(&pkey, w, sizeof(w));
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_write_pubkey_pem(&pkey, w, sizeof(w));
+ } else {
+ ret = mbedtls_pk_write_key_pem(&pkey, w, sizeof(w));
+ }
if (ret != 0) {
memdelete(f);
- memset(w, 0, sizeof(w)); // Zeroize anything we might have written.
+ mbedtls_platform_zeroize(w, sizeof(w)); // Zeroize anything we might have written.
ERR_FAIL_V_MSG(FAILED, "Error writing key '" + itos(ret) + "'.");
}
size_t len = strlen((char *)w);
f->store_buffer(w, len);
memdelete(f);
- memset(w, 0, sizeof(w)); // Zeroize temporary buffer.
+ mbedtls_platform_zeroize(w, sizeof(w)); // Zeroize temporary buffer.
return OK;
}
+Error CryptoKeyMbedTLS::load_from_string(String p_string_key, bool p_public_only) {
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_parse_public_key(&pkey, (unsigned char *)p_string_key.utf8().get_data(), p_string_key.utf8().size());
+ } else {
+ ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)p_string_key.utf8().get_data(), p_string_key.utf8().size(), nullptr, 0);
+ }
+ ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing key '" + itos(ret) + "'.");
+
+ public_only = p_public_only;
+ return OK;
+}
+
+String CryptoKeyMbedTLS::save_to_string(bool p_public_only) {
+ unsigned char w[16000];
+ memset(w, 0, sizeof(w));
+
+ int ret = 0;
+ if (p_public_only) {
+ ret = mbedtls_pk_write_pubkey_pem(&pkey, w, sizeof(w));
+ } else {
+ ret = mbedtls_pk_write_key_pem(&pkey, w, sizeof(w));
+ }
+ if (ret != 0) {
+ mbedtls_platform_zeroize(w, sizeof(w));
+ ERR_FAIL_V_MSG("", "Error saving key '" + itos(ret) + "'.");
+ }
+ String s = String::utf8((char *)w);
+ return s;
+}
+
X509Certificate *X509CertificateMbedTLS::create() {
return memnew(X509CertificateMbedTLS);
}
@@ -108,11 +147,8 @@ Error X509CertificateMbedTLS::load(String p_path) {
int flen = f->get_len();
out.resize(flen + 1);
- {
- uint8_t *w = out.ptrw();
- f->get_buffer(w, flen);
- w[flen] = 0; //end f string
- }
+ f->get_buffer(out.ptrw(), flen);
+ out.write[flen] = 0; // string terminator
memdelete(f);
int ret = mbedtls_x509_crt_parse(&cert, out.ptr(), out.size());
@@ -211,9 +247,8 @@ void CryptoMbedTLS::load_default_certificates(String p_path) {
// Use builtin certs only if user did not override it in project settings.
PackedByteArray out;
out.resize(_certs_uncompressed_size + 1);
- uint8_t *w = out.ptrw();
- Compression::decompress(w, _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
- w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
+ Compression::decompress(out.ptrw(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
+ out.write[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
#ifdef DEBUG_ENABLED
print_verbose("Loaded builtin certs");
#endif
@@ -228,6 +263,7 @@ Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) {
int ret = mbedtls_pk_setup(&(out->pkey), mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
ERR_FAIL_COND_V(ret != 0, nullptr);
ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(out->pkey), mbedtls_ctr_drbg_random, &ctr_drbg, p_bytes, 65537);
+ out->public_only = false;
ERR_FAIL_COND_V(ret != 0, nullptr);
return out;
}
@@ -258,20 +294,15 @@ Ref<X509Certificate> CryptoMbedTLS::generate_self_signed_certificate(Ref<CryptoK
unsigned char buf[4096];
memset(buf, 0, 4096);
- Ref<X509CertificateMbedTLS> out;
- out.instance();
- mbedtls_x509write_crt_pem(&crt, buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg);
-
- int err = mbedtls_x509_crt_parse(&(out->cert), buf, 4096);
- if (err != 0) {
- mbedtls_mpi_free(&serial);
- mbedtls_x509write_crt_free(&crt);
- ERR_PRINT("Generated invalid certificate: " + itos(err));
- return nullptr;
- }
-
+ int ret = mbedtls_x509write_crt_pem(&crt, buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg);
mbedtls_mpi_free(&serial);
mbedtls_x509write_crt_free(&crt);
+ ERR_FAIL_COND_V_MSG(ret != 0, nullptr, "Failed to generate certificate: " + itos(ret));
+ buf[4095] = '\0'; // Make sure strlen can't fail.
+
+ Ref<X509CertificateMbedTLS> out;
+ out.instance();
+ out->load_from_memory(buf, strlen((char *)buf) + 1); // Use strlen to find correct output size.
return out;
}
@@ -281,3 +312,75 @@ PackedByteArray CryptoMbedTLS::generate_random_bytes(int p_bytes) {
mbedtls_ctr_drbg_random(&ctr_drbg, out.ptrw(), p_bytes);
return out;
}
+
+mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) {
+ switch (p_hash_type) {
+ case HashingContext::HASH_MD5:
+ r_size = 16;
+ return MBEDTLS_MD_MD5;
+ case HashingContext::HASH_SHA1:
+ r_size = 20;
+ return MBEDTLS_MD_SHA1;
+ case HashingContext::HASH_SHA256:
+ r_size = 32;
+ return MBEDTLS_MD_SHA256;
+ default:
+ r_size = 0;
+ ERR_FAIL_V_MSG(MBEDTLS_MD_NONE, "Invalid hash type.");
+ }
+}
+
+Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) {
+ int size;
+ mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, Vector<uint8_t>(), "Invalid hash type.");
+ ERR_FAIL_COND_V_MSG(p_hash.size() != size, Vector<uint8_t>(), "Invalid hash provided. Size must be " + itos(size));
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), Vector<uint8_t>(), "Invalid key provided.");
+ ERR_FAIL_COND_V_MSG(key->is_public_only(), Vector<uint8_t>(), "Invalid key provided. Cannot sign with public_only keys.");
+ size_t sig_size = 0;
+ unsigned char buf[MBEDTLS_MPI_MAX_SIZE];
+ Vector<uint8_t> out;
+ int ret = mbedtls_pk_sign(&(key->pkey), type, p_hash.ptr(), size, buf, &sig_size, mbedtls_ctr_drbg_random, &ctr_drbg);
+ ERR_FAIL_COND_V_MSG(ret, out, "Error while signing: " + itos(ret));
+ out.resize(sig_size);
+ copymem(out.ptrw(), buf, sig_size);
+ return out;
+}
+
+bool CryptoMbedTLS::verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) {
+ int size;
+ mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, false, "Invalid hash type.");
+ ERR_FAIL_COND_V_MSG(p_hash.size() != size, false, "Invalid hash provided. Size must be " + itos(size));
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), false, "Invalid key provided.");
+ return mbedtls_pk_verify(&(key->pkey), type, p_hash.ptr(), size, p_signature.ptr(), p_signature.size()) == 0;
+}
+
+Vector<uint8_t> CryptoMbedTLS::encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext) {
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), Vector<uint8_t>(), "Invalid key provided.");
+ uint8_t buf[1024];
+ size_t size;
+ Vector<uint8_t> out;
+ int ret = mbedtls_pk_encrypt(&(key->pkey), p_plaintext.ptr(), p_plaintext.size(), buf, &size, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
+ ERR_FAIL_COND_V_MSG(ret, out, "Error while encrypting: " + itos(ret));
+ out.resize(size);
+ copymem(out.ptrw(), buf, size);
+ return out;
+}
+
+Vector<uint8_t> CryptoMbedTLS::decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext) {
+ Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
+ ERR_FAIL_COND_V_MSG(!key.is_valid(), Vector<uint8_t>(), "Invalid key provided.");
+ ERR_FAIL_COND_V_MSG(key->is_public_only(), Vector<uint8_t>(), "Invalid key provided. Cannot decrypt using a public_only key.");
+ uint8_t buf[2048];
+ size_t size;
+ Vector<uint8_t> out;
+ int ret = mbedtls_pk_decrypt(&(key->pkey), p_ciphertext.ptr(), p_ciphertext.size(), buf, &size, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
+ ERR_FAIL_COND_V_MSG(ret, out, "Error while decrypting: " + itos(ret));
+ out.resize(size);
+ copymem(out.ptrw(), buf, size);
+ return out;
+}
diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h
index 48855d082a..2a446f9d48 100644
--- a/modules/mbedtls/crypto_mbedtls.h
+++ b/modules/mbedtls/crypto_mbedtls.h
@@ -43,15 +43,19 @@ class SSLContextMbedTLS;
class CryptoKeyMbedTLS : public CryptoKey {
private:
mbedtls_pk_context pkey;
- int locks;
+ int locks = 0;
+ bool public_only = true;
public:
static CryptoKey *create();
static void make_default() { CryptoKey::_create = create; }
static void finalize() { CryptoKey::_create = nullptr; }
- virtual Error load(String p_path);
- virtual Error save(String p_path);
+ virtual Error load(String p_path, bool p_public_only);
+ virtual Error save(String p_path, bool p_public_only);
+ virtual String save_to_string(bool p_public_only);
+ virtual Error load_from_string(String p_string_key, bool p_public_only);
+ virtual bool is_public_only() const { return public_only; };
CryptoKeyMbedTLS() {
mbedtls_pk_init(&pkey);
@@ -102,6 +106,7 @@ private:
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
static X509CertificateMbedTLS *default_certs;
+ mbedtls_md_type_t _md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size);
public:
static Crypto *create();
@@ -113,6 +118,10 @@ public:
virtual PackedByteArray generate_random_bytes(int p_bytes);
virtual Ref<CryptoKey> generate_rsa(int p_bytes);
virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after);
+ virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key);
+ virtual bool verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key);
+ virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext);
+ virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext);
CryptoMbedTLS();
~CryptoMbedTLS();
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index eebb3af709..a2fb443ef0 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -448,12 +448,6 @@ void MobileVRInterface::process() {
};
};
-void MobileVRInterface::notification(int p_what){
- _THREAD_SAFE_METHOD_
-
- // nothing to do here, I guess we could pauze our sensors...
-}
-
MobileVRInterface::MobileVRInterface() {
initialized = false;
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index 3a9ed1314a..9b03fff777 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -128,21 +128,21 @@ public:
void set_k2(const real_t p_k2);
real_t get_k2() const;
- virtual StringName get_name() const;
- virtual int get_capabilities() const;
+ virtual StringName get_name() const override;
+ virtual int get_capabilities() const override;
- virtual bool is_initialized() const;
- virtual bool initialize();
- virtual void uninitialize();
+ virtual bool is_initialized() const override;
+ virtual bool initialize() override;
+ virtual void uninitialize() override;
- virtual Size2 get_render_targetsize();
- virtual bool is_stereo();
- virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform);
- virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far);
- virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect);
+ virtual Size2 get_render_targetsize() override;
+ virtual bool is_stereo() override;
+ virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override;
+ virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
+ virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
- virtual void process();
- virtual void notification(int p_what);
+ virtual void process() override;
+ virtual void notification(int p_what) override {}
MobileVRInterface();
~MobileVRInterface();
diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp
index 75638d47c4..0bb555e780 100644
--- a/modules/mobile_vr/register_types.cpp
+++ b/modules/mobile_vr/register_types.cpp
@@ -35,9 +35,11 @@
void register_mobile_vr_types() {
ClassDB::register_class<MobileVRInterface>();
- Ref<MobileVRInterface> mobile_vr;
- mobile_vr.instance();
- XRServer::get_singleton()->add_interface(mobile_vr);
+ if (XRServer::get_singleton()) {
+ Ref<MobileVRInterface> mobile_vr;
+ mobile_vr.instance();
+ XRServer::get_singleton()->add_interface(mobile_vr);
+ }
}
void unregister_mobile_vr_types() {
diff --git a/modules/modules_builders.py b/modules/modules_builders.py
index e7be6380d1..2243162555 100644
--- a/modules/modules_builders.py
+++ b/modules/modules_builders.py
@@ -12,5 +12,16 @@ def generate_modules_enabled(target, source, env):
f.write("#define %s\n" % ("MODULE_" + module.upper() + "_ENABLED"))
+def generate_modules_tests(target, source, env):
+ import os
+ import glob
+
+ with open(target[0].path, "w") as f:
+ for name, path in env.module_list.items():
+ headers = glob.glob(os.path.join(path, "tests", "*.h"))
+ for h in headers:
+ f.write('#include "%s"\n' % (os.path.normpath(h)))
+
+
if __name__ == "__main__":
subprocess_main(globals())
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index c723b210cb..e8f3174a0a 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -29,7 +29,7 @@ if env_mono["tools"] or env_mono["target"] != "release":
mono_configure.configure(env, env_mono)
-if env_mono["tools"] and env_mono["mono_glue"]:
+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
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
index 7391e8790d..3bbbf29d3b 100644
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ b/modules/mono/build_scripts/godot_tools_build.py
@@ -15,7 +15,9 @@ def build_godot_tools(source, target, env):
from .solution_builder import build_solution
- build_solution(env, solution_path, build_config)
+ 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.
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 23f01b3cca..6057004166 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -1,6 +1,5 @@
import os
import os.path
-import sys
import subprocess
from SCons.Script import Dir, Environment
@@ -125,7 +124,8 @@ def configure(env, env_mono):
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"
+ "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,
@@ -191,17 +191,16 @@ def configure(env, env_mono):
env.Append(LIBS=["psapi"])
env.Append(LIBS=["version"])
else:
- mono_lib_name = find_name_in_dir_files(
- mono_lib_path, mono_lib_names, prefixes=["", "lib"], extensions=lib_suffixes
- )
+ mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes)
- if not mono_lib_name:
+ if not mono_lib_file:
raise RuntimeError("Could not find mono library in: " + mono_lib_path)
if env.msvc:
- env.Append(LINKFLAGS=mono_lib_name + ".lib")
+ env.Append(LINKFLAGS=mono_lib_file)
else:
- env.Append(LIBS=[mono_lib_name])
+ mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file)
+ env.Append(LINKFLAGS=mono_lib_file_path)
mono_bin_path = os.path.join(mono_root, "bin")
@@ -397,9 +396,8 @@ def configure(env, env_mono):
mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
if tools_enabled:
+ # Only supported for editor builds.
copy_mono_root_files(env, mono_root)
- else:
- print("Ignoring option: 'copy_mono_root'; only available for builds with 'tools' enabled.")
def make_template_dir(env, mono_root):
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
index 3090a4759a..0ec7e2f433 100644
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -9,7 +9,7 @@ if os.name == "nt":
def _reg_open_key(key, subkey):
try:
return winreg.OpenKey(key, subkey)
- except (WindowsError, OSError):
+ except OSError:
if platform.architecture()[0] == "32bit":
bitness_sam = winreg.KEY_WOW64_64KEY
else:
@@ -37,7 +37,7 @@ def _find_mono_in_reg(subkey, bits):
with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0]
return value
- except (WindowsError, OSError):
+ except OSError:
return None
@@ -48,7 +48,7 @@ def _find_mono_in_reg_old(subkey, bits):
if default_clr:
return _find_mono_in_reg(subkey + "\\" + default_clr, bits)
return None
- except (WindowsError, EnvironmentError):
+ except OSError:
return None
@@ -97,7 +97,7 @@ def find_msbuild_tools_path_reg():
raise ValueError("Cannot find `installationPath` entry")
except ValueError as e:
print("Error reading output from vswhere: " + e.message)
- except WindowsError:
+ except OSError:
pass # Fine, vswhere not found
except (subprocess.CalledProcessError, OSError):
pass
@@ -109,5 +109,5 @@ def find_msbuild_tools_path_reg():
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0]
return value
- except (WindowsError, OSError):
+ except OSError:
return ""
diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py
index 371819fd72..6a621c3c8b 100644
--- a/modules/mono/build_scripts/solution_builder.py
+++ b/modules/mono/build_scripts/solution_builder.py
@@ -8,9 +8,6 @@ def find_dotnet_cli():
import os.path
if os.name == "nt":
- windows_exts = os.environ["PATHEXT"]
- windows_exts = windows_exts.split(os.pathsep) if windows_exts else []
-
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
@@ -142,9 +139,7 @@ def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
# Build solution
- targets = ["Restore", "Build"]
-
- msbuild_args += [solution_path, "/t:%s" % ",".join(targets), "/p:Configuration=" + build_config]
+ 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/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index 39e3a95afa..d7b2028204 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -53,8 +53,9 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
ClassDB::ClassInfo *t = ClassDB::classes.getptr(E->get());
ERR_FAIL_COND(!t);
- if (t->api != p_api || !t->exposed)
+ if (t->api != p_api || !t->exposed) {
continue;
+ }
Dictionary class_dict;
classes_dict[t->name] = class_dict;
@@ -72,8 +73,9 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
ERR_CONTINUE(name.empty());
- if (name[0] == '_')
+ if (name[0] == '_') {
continue; // Ignore non-virtual methods that start with an underscore
+ }
snames.push_back(*k);
}
diff --git a/modules/mono/config.py b/modules/mono/config.py
index d41f3755b5..d060ae9b28 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -23,17 +23,16 @@ def configure(env):
envvars.Add(
PathVariable(
"mono_prefix",
- "Path to the mono installation directory for the target platform and architecture",
+ "Path to the Mono installation directory for the target platform and architecture",
"",
PathVariable.PathAccept,
)
)
- envvars.Add(BoolVariable("mono_static", "Statically link mono", default_mono_static))
- envvars.Add(BoolVariable("mono_glue", "Build with the mono glue sources", True))
+ envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static))
+ envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True))
+ envvars.Add(BoolVariable("build_cil", "Build C# solutions", True))
envvars.Add(
- BoolVariable(
- "copy_mono_root", "Make a copy of the mono installation directory to bundle with the editor", False
- )
+ BoolVariable("copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True)
)
# TODO: It would be great if this could be detected automatically instead
@@ -56,7 +55,6 @@ def configure(env):
def get_doc_classes():
return [
- "@C#",
"CSharpScript",
"GodotSharp",
]
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index a5aae5175b..a05b38b8bf 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -44,7 +44,6 @@
#ifdef TOOLS_ENABLED
#include "editor/bindings_generator.h"
-#include "editor/csharp_project.h"
#include "editor/editor_node.h"
#include "editor/node_dock.h"
#endif
@@ -125,8 +124,9 @@ void CSharpLanguage::init() {
print_line("Run this binary with '--generate-mono-glue path/to/modules/mono/glue'");
#endif
- if (gdmono->is_runtime_initialized())
+ if (gdmono->is_runtime_initialized()) {
gdmono->initialize_load_assemblies();
+ }
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&_editor_init_callback);
@@ -134,8 +134,13 @@ void CSharpLanguage::init() {
}
void CSharpLanguage::finish() {
- if (finalized)
+ finalize();
+}
+
+void CSharpLanguage::finalize() {
+ if (finalized) {
return;
+ }
finalizing = true;
@@ -390,15 +395,17 @@ bool CSharpLanguage::supports_builtin_mode() const {
#ifdef TOOLS_ENABLED
static String variant_type_to_managed_name(const String &p_var_type_name) {
- if (p_var_type_name.empty())
+ if (p_var_type_name.empty()) {
return "object";
+ }
if (!ClassDB::class_exists(p_var_type_name)) {
return p_var_type_name;
}
- if (p_var_type_name == Variant::get_type_name(Variant::OBJECT))
+ if (p_var_type_name == Variant::get_type_name(Variant::OBJECT)) {
return "Godot.Object";
+ }
if (p_var_type_name == Variant::get_type_name(Variant::FLOAT)) {
#ifdef REAL_T_IS_DOUBLE
@@ -408,36 +415,49 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
#endif
}
- if (p_var_type_name == Variant::get_type_name(Variant::STRING))
+ if (p_var_type_name == Variant::get_type_name(Variant::STRING)) {
return "string"; // I prefer this one >:[
+ }
- if (p_var_type_name == Variant::get_type_name(Variant::DICTIONARY))
+ if (p_var_type_name == Variant::get_type_name(Variant::DICTIONARY)) {
return "Collections.Dictionary";
+ }
- if (p_var_type_name == Variant::get_type_name(Variant::ARRAY))
+ if (p_var_type_name == Variant::get_type_name(Variant::ARRAY)) {
return "Collections.Array";
+ }
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_BYTE_ARRAY))
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_BYTE_ARRAY)) {
return "byte[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT32_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT32_ARRAY)) {
return "int[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT64_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_INT64_ARRAY)) {
return "long[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) {
return "float[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY)) {
return "double[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_STRING_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_STRING_ARRAY)) {
return "string[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY)) {
return "Vector2[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR3_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_VECTOR3_ARRAY)) {
return "Vector3[]";
- if (p_var_type_name == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY))
+ }
+ if (p_var_type_name == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY)) {
return "Color[]";
+ }
- if (p_var_type_name == Variant::get_type_name(Variant::SIGNAL))
+ if (p_var_type_name == Variant::get_type_name(Variant::SIGNAL)) {
return "SignalInfo";
+ }
Variant::Type var_types[] = {
Variant::BOOL,
@@ -462,8 +482,9 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
};
for (unsigned int i = 0; i < sizeof(var_types) / sizeof(Variant::Type); i++) {
- if (p_var_type_name == Variant::get_type_name(var_types[i]))
+ if (p_var_type_name == Variant::get_type_name(var_types[i])) {
return p_var_type_name;
+ }
}
return "object";
@@ -477,8 +498,9 @@ String CSharpLanguage::make_function(const String &, const String &p_name, const
for (int i = 0; i < p_args.size(); i++) {
const String &arg = p_args[i];
- if (i > 0)
+ if (i > 0) {
s += ", ";
+ }
s += variant_type_to_managed_name(arg.get_slice(":", 1)) + " " + escape_csharp_keyword(arg.get_slice(":", 0));
}
@@ -516,32 +538,36 @@ String CSharpLanguage::debug_get_error() const {
}
int CSharpLanguage::debug_get_stack_level_count() const {
- if (_debug_parse_err_line >= 0)
+ if (_debug_parse_err_line >= 0) {
return 1;
+ }
// TODO: StackTrace
return 1;
}
int CSharpLanguage::debug_get_stack_level_line(int p_level) const {
- if (_debug_parse_err_line >= 0)
+ if (_debug_parse_err_line >= 0) {
return _debug_parse_err_line;
+ }
// TODO: StackTrace
return 1;
}
String CSharpLanguage::debug_get_stack_level_function(int p_level) const {
- if (_debug_parse_err_line >= 0)
+ if (_debug_parse_err_line >= 0) {
return String();
+ }
// TODO: StackTrace
return String();
}
String CSharpLanguage::debug_get_stack_level_source(int p_level) const {
- if (_debug_parse_err_line >= 0)
+ if (_debug_parse_err_line >= 0) {
return _debug_parse_err_file;
+ }
// TODO: StackTrace
return String();
@@ -551,15 +577,17 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED
// Printing an error here will result in endless recursion, so we must be careful
static thread_local bool _recursion_flag_ = false;
- if (_recursion_flag_)
+ if (_recursion_flag_) {
return Vector<StackInfo>();
+ }
_recursion_flag_ = true;
SCOPE_EXIT { _recursion_flag_ = false; };
GD_MONO_SCOPE_THREAD_ATTACH;
- if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated)
+ if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated) {
return Vector<StackInfo>();
+ }
MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
@@ -581,8 +609,9 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
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_)
+ if (_recursion_flag_) {
return Vector<StackInfo>();
+ }
_recursion_flag_ = true;
SCOPE_EXIT { _recursion_flag_ = false; };
@@ -599,8 +628,9 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
int frame_count = mono_array_length(frames);
- if (frame_count <= 0)
+ if (frame_count <= 0) {
return Vector<StackInfo>();
+ }
Vector<StackInfo> si;
si.resize(frame_count);
@@ -646,8 +676,9 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) {
ObjectID id = p_obj->get_instance_id();
Map<ObjectID, int>::Element *elem = unsafe_object_references.find(id);
ERR_FAIL_NULL(elem);
- if (--elem->value() == 0)
+ if (--elem->value() == 0) {
unsafe_object_references.erase(elem);
+ }
#endif
}
@@ -673,8 +704,9 @@ void CSharpLanguage::frame() {
struct CSharpScriptDepSort {
// 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)
+ if (A == B) {
return false; // shouldn't happen but..
+ }
GDMonoClass *I = B->base;
while (I) {
if (I == A->script_class) {
@@ -717,8 +749,9 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
#ifdef GD_MONO_HOT_RELOAD
bool CSharpLanguage::is_assembly_reloading_needed() {
- if (!gdmono->is_runtime_initialized())
+ if (!gdmono->is_runtime_initialized()) {
return false;
+ }
GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
@@ -736,23 +769,27 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
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))
+ if (!FileAccess::exists(proj_asm_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(proj_asm_path) <= proj_assembly->get_modified_time()) {
return false; // Already up to date
+ }
} else {
- if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe)))
+ if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe))) {
return false; // No assembly to load
+ }
}
return true;
}
void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
- if (!gdmono->is_runtime_initialized())
+ if (!gdmono->is_runtime_initialized()) {
return;
+ }
// There is no soft reloading with Mono. It's always hard reloading.
@@ -858,8 +895,9 @@ 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_multilevel(string_names.on_before_serialize);
+ if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
+ obj->get_script_instance()->call(string_names.on_before_serialize);
+ }
// Save instance info
CSharpScript::StateBackup state;
@@ -894,8 +932,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
for (const Map<ObjectID, CSharpScript::StateBackup>::Element *F = scr->pending_reload_state.front(); F; F = F->next()) {
Object *obj = ObjectDB::get_instance(F->key());
- if (!obj)
+ if (!obj) {
continue;
+ }
ObjectID obj_id = obj->get_instance_id();
@@ -1092,8 +1131,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
// Call OnAfterDeserialization
- if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
- obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
+ if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
+ obj->get_script_instance()->call(string_names.on_after_deserialize);
+ }
}
}
@@ -1246,6 +1286,7 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
script_binding.inited = false;
}
+#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(ManagedCallable::instances_mutex);
@@ -1255,6 +1296,7 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
managed_callable->delegate_invoke = nullptr;
}
}
+#endif
scripts_metadata_invalidated = true;
}
@@ -1275,7 +1317,8 @@ void CSharpLanguage::_editor_init_callback() {
GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc);
UNHANDLED_EXCEPTION(exc);
- EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(GDMonoMarshal::mono_object_to_variant(mono_object));
+ EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(
+ GDMonoMarshal::mono_object_to_variant(mono_object).operator Object *());
CRASH_COND(godotsharp_editor == nullptr);
// Enable it as a plugin
@@ -1324,7 +1367,7 @@ CSharpLanguage::CSharpLanguage() {
}
CSharpLanguage::~CSharpLanguage() {
- finish();
+ finalize();
singleton = nullptr;
}
@@ -1341,8 +1384,9 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
// ¯\_(ツ)_/¯
const ClassDB::ClassInfo *classinfo = ClassDB::classes.getptr(type_name);
- while (classinfo && !classinfo->exposed)
+ while (classinfo && !classinfo->exposed) {
classinfo = classinfo->inherits_ptr;
+ }
ERR_FAIL_NULL_V(classinfo, false);
type_name = classinfo->name;
@@ -1380,13 +1424,15 @@ void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
MutexLock lock(language_bind_mutex);
Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object);
- if (match)
+ if (match) {
return (void *)match;
+ }
CSharpScriptBinding script_binding;
- if (!setup_csharp_script_binding(script_binding, p_object))
+ if (!setup_csharp_script_binding(script_binding, p_object)) {
return nullptr;
+ }
return (void *)insert_script_binding(p_object, script_binding);
}
@@ -1404,8 +1450,9 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
return;
}
- if (finalizing)
+ if (finalizing) {
return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there
+ }
GD_MONO_ASSERT_THREAD_ATTACHED;
@@ -1444,8 +1491,9 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
MonoGCHandleData &gchandle = script_binding.gchandle;
- if (!script_binding.inited)
+ if (!script_binding.inited) {
return;
+ }
if (ref_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;
@@ -1455,8 +1503,9 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
// so the owner must hold the managed side alive again to avoid it from being GCed.
MonoObject *target = gchandle.get_target();
- if (!target)
+ if (!target) {
return; // 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);
@@ -1481,8 +1530,9 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
int refcount = ref_owner->reference_get_count();
- if (!script_binding.inited)
+ if (!script_binding.inited) {
return refcount == 0;
+ }
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;
@@ -1491,8 +1541,9 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
// the managed instance takes responsibility of deleting the owner when GCed.
MonoObject *target = gchandle.get_target();
- if (!target)
+ if (!target) {
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);
@@ -1514,8 +1565,9 @@ CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpS
instance->owner = p_owner;
instance->gchandle = p_gchandle;
- if (instance->base_ref)
+ if (instance->base_ref) {
instance->_reference_owner_unsafe();
+ }
p_script->instances.insert(p_owner);
@@ -1572,8 +1624,9 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
MonoObject *ret = method->invoke(mono_object, args);
- if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret))
+ if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret)) {
return true;
+ }
break;
}
@@ -1658,8 +1711,9 @@ void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Va
ManagedType managedType;
GDMonoField *field = script->script_class->get_field(state_pair.first);
- if (!field)
+ if (!field) {
continue; // Properties ignored. We get the property baking fields instead.
+ }
managedType = field->get_type();
@@ -1679,8 +1733,9 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
const CSharpScript::EventSignal &event_signal = E->value();
MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
- if (!delegate_field_value)
+ if (!delegate_field_value) {
continue; // Empty
+ }
Array serialized_data;
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
@@ -1725,8 +1780,9 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
if (ret) {
Array array = Array(GDMonoMarshal::mono_object_to_variant(ret));
- for (int i = 0, size = array.size(); i < size; i++)
+ for (int i = 0, size = array.size(); i < size; i++) {
p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
+ }
return;
}
@@ -1739,20 +1795,23 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
if (script->member_info.has(p_name)) {
- if (r_is_valid)
+ if (r_is_valid) {
*r_is_valid = true;
+ }
return script->member_info[p_name].type;
}
- if (r_is_valid)
+ if (r_is_valid) {
*r_is_valid = false;
+ }
return Variant::NIL;
}
bool CSharpInstance::has_method(const StringName &p_method) const {
- if (!script.is_valid())
+ if (!script.is_valid()) {
return false;
+ }
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -1806,41 +1865,6 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args,
return Variant();
}
-void CSharpInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- if (script.is_valid()) {
- MonoObject *mono_object = get_mono_object();
-
- ERR_FAIL_NULL(mono_object);
-
- _call_multilevel(mono_object, p_method, p_args, p_argcount);
- }
-}
-
-void CSharpInstance::_call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- GDMonoClass *top = script->script_class;
-
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(p_method, p_argcount);
-
- if (method) {
- method->invoke(p_mono_object, p_args);
- return;
- }
-
- top = top->get_parent_class();
- }
-}
-
-void CSharpInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
- // Sorry, the method is the one that controls the call order
-
- call_multilevel(p_method, p_args, p_argcount);
-}
-
bool CSharpInstance::_reference_owner_unsafe() {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref);
@@ -1868,8 +1892,9 @@ bool CSharpInstance::_unreference_owner_unsafe() {
CRASH_COND(owner == nullptr);
#endif
- if (!unsafe_referenced)
+ if (!unsafe_referenced) {
return false; // Already unreferenced
+ }
unsafe_referenced = false;
@@ -1902,7 +1927,7 @@ MonoObject *CSharpInstance::_internal_new_managed() {
bool die = _unreference_owner_unsafe();
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
- CRASH_COND(die == true);
+ CRASH_COND(die);
owner = nullptr;
@@ -1912,8 +1937,9 @@ MonoObject *CSharpInstance::_internal_new_managed() {
// Tie managed to unmanaged
gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (base_ref)
+ if (base_ref) {
_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
+ }
CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner);
@@ -2130,7 +2156,7 @@ void CSharpInstance::_call_notification(int p_notification) {
// Custom version of _call_multilevel, optimized for _notification
- uint32_t arg = p_notification;
+ int32_t arg = p_notification;
void *args[1] = { &arg };
StringName method_name = CACHED_STRING_NAME(_notification);
@@ -2154,8 +2180,9 @@ String CSharpInstance::to_string(bool *r_valid) {
MonoObject *mono_object = get_mono_object();
if (mono_object == nullptr) {
- if (r_valid)
+ if (r_valid) {
*r_valid = false;
+ }
return String();
}
@@ -2164,14 +2191,16 @@ String CSharpInstance::to_string(bool *r_valid) {
if (exc) {
GDMonoUtils::set_pending_exception(exc);
- if (r_valid)
+ if (r_valid) {
*r_valid = false;
+ }
return String();
}
if (result == nullptr) {
- if (r_valid)
+ if (r_valid) {
*r_valid = false;
+ }
return String();
}
@@ -2233,7 +2262,7 @@ CSharpInstance::~CSharpInstance() {
// Unreference the owner here, before the new "instance binding" references it.
// Otherwise, the unsafe reference debug checks will incorrectly detect a bug.
bool die = _unreference_owner_unsafe();
- CRASH_COND(die == true); // `owner_keep_alive` holds a reference, so it can't die
+ CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die
void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
CRASH_COND(data == nullptr);
@@ -2342,11 +2371,13 @@ void CSharpScript::_update_member_info_no_exports() {
bool CSharpScript::_update_exports() {
#ifdef TOOLS_ENABLED
bool is_editor = Engine::get_singleton()->is_editor_hint();
- if (is_editor)
+ if (is_editor) {
placeholder_fallback_enabled = true; // until proven otherwise
+ }
#endif
- if (!valid)
+ if (!valid) {
return false;
+ }
bool changed = false;
@@ -2419,15 +2450,22 @@ bool CSharpScript::_update_exports() {
StringName member_name = field->get_name();
member_info[member_name] = prop_info;
+
+ if (exported) {
#ifdef TOOLS_ENABLED
- if (is_editor && exported) {
- exported_members_cache.push_front(prop_info);
+ if (is_editor) {
+ exported_members_cache.push_front(prop_info);
- if (tmp_object) {
- exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
+ if (tmp_object) {
+ exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
+ }
}
- }
#endif
+
+#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
+ exported_members_names.insert(member_name);
+#endif
+ }
}
}
@@ -2440,21 +2478,28 @@ bool CSharpScript::_update_exports() {
StringName member_name = property->get_name();
member_info[member_name] = prop_info;
+
+ if (exported) {
#ifdef TOOLS_ENABLED
- if (is_editor && exported) {
- exported_members_cache.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);
+ if (is_editor) {
+ exported_members_cache.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);
+ }
}
}
- }
#endif
+
+#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
+ exported_members_names.insert(member_name);
+#endif
+ }
}
}
@@ -2529,8 +2574,9 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
for (int i = delegates.size() - 1; i >= 0; --i) {
GDMonoClass *delegate = delegates[i];
- if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute)))
+ 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()));
@@ -2563,11 +2609,13 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
GDMonoClass *field_class = field->get_type().type_class;
- if (!mono_class_is_delegate(field_class->get_mono_ptr()))
+ if (!mono_class_is_delegate(field_class->get_mono_ptr())) {
continue;
+ }
- if (!found_event_signals.find(field->get_name()))
+ 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()));
@@ -2626,14 +2674,16 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
if (p_member->is_static()) {
#ifdef TOOLS_ENABLED
- if (p_member->has_attribute(CACHED_CLASS(ExportAttribute)))
+ 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()))
+ if (member_info.has(p_member->get_name())) {
return false;
+ }
ManagedType type;
@@ -2651,15 +2701,17 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
if (!property->has_getter()) {
#ifdef TOOLS_ENABLED
- if (exported)
- ERR_PRINT("Read-only property cannot be exported: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
+ 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("Write-only property (without getter) cannot be exported: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
+ if (exported) {
+ ERR_PRINT("Cannot export a property without a setter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
+ }
#endif
return false;
}
@@ -2674,7 +2726,9 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
return true;
}
+#ifdef TOOLS_ENABLED
MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
+#endif
PropertyHint hint = PROPERTY_HINT_NONE;
String hint_string;
@@ -2786,8 +2840,9 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
ManagedType elem_type;
- if (!GDMonoMarshal::try_get_array_element_type(p_type, 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);
@@ -2814,15 +2869,6 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
}
#endif
-void CSharpScript::_clear() {
- tool = false;
- valid = false;
-
- base = nullptr;
- native = nullptr;
- script_class = nullptr;
-}
-
Variant CSharpScript::call(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.
@@ -2855,11 +2901,7 @@ Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, i
}
void CSharpScript::_resource_path_changed() {
- String path = get_path();
-
- if (!path.empty()) {
- name = get_path().get_file().get_basename();
- }
+ _update_name();
}
bool CSharpScript::_get(const StringName &p_name, Variant &r_ret) const {
@@ -2915,8 +2957,9 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
GDMonoClass *base = p_script->script_class->get_parent_class();
- if (base != p_script->native)
+ if (base != p_script->native) {
p_script->base = base;
+ }
p_script->valid = true;
p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
@@ -2942,8 +2985,9 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
while (native_top) {
native_top->fetch_methods_with_godot_api_checks(p_script->native);
- if (native_top == CACHED_CLASS(GodotObject))
+ if (native_top == CACHED_CLASS(GodotObject)) {
break;
+ }
native_top = native_top->get_parent_class();
}
@@ -2989,10 +3033,11 @@ bool CSharpScript::can_instance() const {
}
StringName CSharpScript::get_instance_base_type() const {
- if (native)
+ if (native) {
return native->get_name();
- else
+ } else {
return StringName();
+ }
}
CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) {
@@ -3055,7 +3100,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
bool die = instance->_unreference_owner_unsafe();
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
- CRASH_COND(die == true);
+ CRASH_COND(die);
p_owner->set_script_instance(nullptr);
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
@@ -3065,8 +3110,9 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Tie managed to unmanaged
instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (instance->base_ref)
+ if (instance->base_ref) {
instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
+ }
{
MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
@@ -3168,8 +3214,9 @@ String CSharpScript::get_source_code() const {
}
void CSharpScript::set_source_code(const String &p_code) {
- if (source == p_code)
+ if (source == p_code) {
return;
+ }
source = p_code;
#ifdef TOOLS_ENABLED
source_changed_cache = true;
@@ -3177,8 +3224,9 @@ void CSharpScript::set_source_code(const String &p_code) {
}
void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
- if (!script_class)
+ if (!script_class) {
return;
+ }
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -3190,8 +3238,9 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
}
bool CSharpScript::has_method(const StringName &p_method) const {
- if (!script_class)
+ if (!script_class) {
return false;
+ }
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -3199,8 +3248,9 @@ bool CSharpScript::has_method(const StringName &p_method) const {
}
MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
- if (!script_class)
+ if (!script_class) {
return MethodInfo();
+ }
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -3240,9 +3290,7 @@ Error CSharpScript::reload(bool p_keep_state) {
ERR_FAIL_NULL_V(namespace_, ERR_BUG);
ERR_FAIL_NULL_V(class_name, ERR_BUG);
GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String());
- if (klass) {
- bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(klass);
- ERR_FAIL_COND_V(!obj_type, ERR_BUG);
+ if (klass && CACHED_CLASS(GodotObject)->is_assignable_from(klass)) {
script_class = klass;
}
} else {
@@ -3276,8 +3324,9 @@ Error CSharpScript::reload(bool p_keep_state) {
GDMonoClass *base_class = script_class->get_parent_class();
- if (base_class != native)
+ if (base_class != native) {
base = base_class;
+ }
#ifdef DEBUG_ENABLED
// For debug builds, we must fetch from all native base methods as well.
@@ -3289,8 +3338,9 @@ Error CSharpScript::reload(bool p_keep_state) {
while (native_top) {
native_top->fetch_methods_with_godot_api_checks(native);
- if (native_top == CACHED_CLASS(GodotObject))
+ if (native_top == CACHED_CLASS(GodotObject)) {
break;
+ }
native_top = native_top->get_parent_class();
}
@@ -3420,8 +3470,9 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
const SignalParameter &param = params[i];
PropertyInfo arg_info = PropertyInfo(param.type, param.name);
- if (param.type == Variant::NIL && param.nil_is_variant)
+ if (param.type == Variant::NIL && param.nil_is_variant) {
arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
mi.arguments.push_back(arg_info);
}
@@ -3439,8 +3490,9 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
const SignalParameter &param = params[i];
PropertyInfo arg_info = PropertyInfo(param.type, param.name);
- if (param.type == Variant::NIL && param.nil_is_variant)
+ if (param.type == Variant::NIL && param.nil_is_variant) {
arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
mi.arguments.push_back(arg_info);
}
@@ -3483,18 +3535,24 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
}
MultiplayerAPI::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
- if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute)))
+ if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) {
return MultiplayerAPI::RPC_MODE_REMOTE;
- if (p_member->has_attribute(CACHED_CLASS(MasterAttribute)))
+ }
+ if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) {
return MultiplayerAPI::RPC_MODE_MASTER;
- if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute)))
+ }
+ if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) {
return MultiplayerAPI::RPC_MODE_PUPPET;
- if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute)))
+ }
+ if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute))) {
return MultiplayerAPI::RPC_MODE_REMOTESYNC;
- if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute)))
+ }
+ if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute))) {
return MultiplayerAPI::RPC_MODE_MASTERSYNC;
- if (p_member->has_attribute(CACHED_CLASS(PuppetSyncAttribute)))
+ }
+ if (p_member->has_attribute(CACHED_CLASS(PuppetSyncAttribute))) {
return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
+ }
return MultiplayerAPI::RPC_MODE_DISABLED;
}
@@ -3569,14 +3627,27 @@ Error CSharpScript::load_source_code(const String &p_path) {
return OK;
}
-StringName CSharpScript::get_script_name() const {
- return name;
+void CSharpScript::_update_name() {
+ String path = get_path();
+
+ if (!path.empty()) {
+ name = get_path().get_file().get_basename();
+ }
+}
+
+void CSharpScript::_clear() {
+ tool = false;
+ valid = false;
+
+ base = nullptr;
+ native = nullptr;
+ script_class = nullptr;
}
CSharpScript::CSharpScript() {
_clear();
- _resource_path_changed();
+ _update_name();
#ifdef DEBUG_ENABLED
{
@@ -3593,11 +3664,22 @@ CSharpScript::~CSharpScript() {
#endif
}
+void CSharpScript::get_members(Set<StringName> *p_members) {
+#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
+ if (p_members) {
+ for (Set<StringName>::Element *E = exported_members_names.front(); E; E = E->next()) {
+ p_members->insert(E->get());
+ }
+ }
+#endif
+}
+
/*************** RESOURCE ***************/
RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
- if (r_error)
+ if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
+ }
// TODO ignore anything inside bin/ and obj/ in tools builds?
@@ -3614,8 +3696,9 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
script->reload();
- if (r_error)
+ if (r_error) {
*r_error = OK;
+ }
return scriptres;
}
@@ -3640,13 +3723,9 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
#ifdef TOOLS_ENABLED
if (!FileAccess::exists(p_path)) {
- // The file does not yet exists, let's assume the user just created this script
-
- if (_create_project_solution_if_needed()) {
- CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
- "Compile",
- ProjectSettings::get_singleton()->globalize_path(p_path));
- } else {
+ // The file does not yet exist, let's assume the user just created this script. In such
+ // cases we need to check whether the solution and csproj were already created or not.
+ if (!_create_project_solution_if_needed()) {
ERR_PRINT("C# project could not be created; cannot add file: '" + p_path + "'.");
}
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 52b0783a6e..f0b43a40f9 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -135,13 +135,19 @@ private:
bool exports_invalidated = true;
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
void _update_member_info_no_exports();
- virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+ void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
+#endif
+
+#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
+ Set<StringName> exported_members_names;
#endif
Map<StringName, PropertyInfo> member_info;
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);
@@ -165,66 +171,66 @@ private:
protected:
static void _bind_methods();
- Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual void _resource_path_changed();
+ Variant call(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:
- virtual bool can_instance() const;
- virtual StringName get_instance_base_type() const;
- virtual ScriptInstance *instance_create(Object *p_this);
- virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this);
- virtual bool instance_has(const Object *p_this) const;
+ bool can_instance() const override;
+ StringName get_instance_base_type() const override;
+ ScriptInstance *instance_create(Object *p_this) override;
+ PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override;
+ bool instance_has(const Object *p_this) const override;
- virtual bool has_source_code() const;
- virtual String get_source_code() const;
- virtual void set_source_code(const String &p_code);
+ bool has_source_code() const override;
+ String get_source_code() const override;
+ void set_source_code(const String &p_code) override;
- virtual Error reload(bool p_keep_state = false);
+ Error reload(bool p_keep_state = false) override;
- virtual bool has_script_signal(const StringName &p_signal) const;
- virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+ bool has_script_signal(const StringName &p_signal) const override;
+ void get_script_signal_list(List<MethodInfo> *r_signals) const override;
- virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
- virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
- virtual void update_exports();
+ bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
+ void get_script_property_list(List<PropertyInfo> *p_list) const override;
+ void update_exports() override;
- virtual bool is_tool() const { return tool; }
- virtual bool is_valid() const { return valid; }
+ void get_members(Set<StringName> *p_members) override;
- bool inherits_script(const Ref<Script> &p_script) const;
+ bool is_tool() const override { return tool; }
+ bool is_valid() const override { return valid; }
- virtual Ref<Script> get_base_script() const;
- virtual ScriptLanguage *get_language() const;
+ bool inherits_script(const Ref<Script> &p_script) const override;
- virtual void get_script_method_list(List<MethodInfo> *p_list) const;
- bool has_method(const StringName &p_method) const;
- MethodInfo get_method_info(const StringName &p_method) const;
+ Ref<Script> get_base_script() const override;
+ ScriptLanguage *get_language() const override;
- virtual int get_member_line(const StringName &p_member) const;
+ void get_script_method_list(List<MethodInfo> *p_list) const override;
+ bool has_method(const StringName &p_method) const override;
+ MethodInfo get_method_info(const StringName &p_method) const override;
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ int get_member_line(const StringName &p_member) const override;
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ Vector<ScriptNetData> get_rpc_methods() const override;
+ uint16_t get_rpc_method_id(const StringName &p_method) const override;
+ StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
+ MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
+ MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
+
+ Vector<ScriptNetData> get_rset_properties() const override;
+ uint16_t get_rset_property_id(const StringName &p_variable) const override;
+ StringName get_rset_property(const uint16_t p_variable_id) const override;
+ MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const override;
+ MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
#ifdef TOOLS_ENABLED
- virtual bool is_placeholder_fallback_enabled() const { return placeholder_fallback_enabled; }
+ bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
#endif
Error load_source_code(const String &p_path);
- StringName get_script_name() const;
-
CSharpScript();
~CSharpScript();
};
@@ -243,8 +249,6 @@ class CSharpInstance : public ScriptInstance {
Ref<CSharpScript> script;
MonoGCHandleData gchandle;
- Vector<Callable> event_signal_callables;
-
bool _reference_owner_unsafe();
/*
@@ -261,8 +265,6 @@ class CSharpInstance : public ScriptInstance {
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 _call_multilevel(MonoObject *p_mono_object, const StringName &p_method, const Variant **p_args, int p_argcount);
-
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);
@@ -271,18 +273,16 @@ public:
_FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; }
- virtual Object *get_owner();
+ Object *get_owner() override;
- virtual bool set(const StringName &p_name, const Variant &p_value);
- virtual bool get(const StringName &p_name, Variant &r_ret) const;
- virtual void get_property_list(List<PropertyInfo> *p_properties) const;
- virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const;
+ bool set(const StringName &p_name, const Variant &p_value) override;
+ bool get(const StringName &p_name, Variant &r_ret) const override;
+ 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;
- /* TODO */ virtual void get_method_list(List<MethodInfo> *p_list) const {}
- virtual bool has_method(const StringName &p_method) const;
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
- virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
+ /* TODO */ void get_method_list(List<MethodInfo> *p_list) const override {}
+ bool has_method(const StringName &p_method) const override;
+ Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
void mono_object_disposed(MonoObject *p_obj);
@@ -295,29 +295,29 @@ public:
void connect_event_signals();
void disconnect_event_signals();
- virtual void refcount_incremented();
- virtual bool refcount_decremented();
+ void refcount_incremented() override;
+ bool refcount_decremented() override;
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ Vector<ScriptNetData> get_rpc_methods() const override;
+ uint16_t get_rpc_method_id(const StringName &p_method) const override;
+ StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
+ MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
+ MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ Vector<ScriptNetData> get_rset_properties() const override;
+ uint16_t get_rset_property_id(const StringName &p_variable) const override;
+ StringName get_rset_property(const uint16_t p_variable_id) const override;
+ MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const override;
+ MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
- virtual void notification(int p_notification);
+ void notification(int p_notification) override;
void _call_notification(int p_notification);
- virtual String to_string(bool *r_valid);
+ String to_string(bool *r_valid) override;
- virtual Ref<Script> get_script() const;
+ Ref<Script> get_script() const override;
- virtual ScriptLanguage *get_language();
+ ScriptLanguage *get_language() override;
CSharpInstance(const Ref<CSharpScript> &p_script);
~CSharpInstance();
@@ -431,83 +431,90 @@ public:
}
_FORCE_INLINE_ const Dictionary &get_scripts_metadata() {
- if (scripts_metadata_invalidated)
+ if (scripts_metadata_invalidated) {
_load_scripts_metadata();
+ }
return scripts_metadata;
}
_FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
- virtual String get_name() const;
+ String get_name() const override;
/* LANGUAGE FUNCTIONS */
- virtual String get_type() const;
- virtual String get_extension() const;
- virtual Error execute_file(const String &p_path);
- virtual void init();
- virtual void finish();
+ String get_type() const override;
+ String get_extension() const override;
+ Error execute_file(const String &p_path) override;
+ void init() override;
+ void finish() override;
+
+ void finalize();
/* EDITOR FUNCTIONS */
- virtual void get_reserved_words(List<String> *p_words) const;
- virtual void get_comment_delimiters(List<String> *p_delimiters) const;
- virtual void get_string_delimiters(List<String> *p_delimiters) const;
- virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool is_using_templates();
- virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const { return true; }
- virtual String validate_path(const String &p_path) const;
- virtual Script *create_script() const;
- virtual bool has_named_classes() const;
- virtual bool supports_builtin_mode() const;
- /* TODO? */ virtual int find_function(const String &p_function, const String &p_code) const { return -1; }
- virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
+ void get_reserved_words(List<String> *p_words) const override;
+ void get_comment_delimiters(List<String> *p_delimiters) const override;
+ void get_string_delimiters(List<String> *p_delimiters) const override;
+ Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const override;
+ bool is_using_templates() override;
+ void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) override;
+ /* TODO */ bool validate(const String &p_script, int &r_line_error, int &r_col_error,
+ String &r_test_error, const String &p_path, List<String> *r_functions,
+ List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override {
+ return true;
+ }
+ String validate_path(const String &p_path) const override;
+ 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; }
+ String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override;
virtual String _get_indentation() const;
- /* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {}
- /* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {}
+ /* TODO? */ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {}
+ /* TODO */ void add_global_constant(const StringName &p_variable, const Variant &p_value) override {}
/* DEBUGGER FUNCTIONS */
- virtual String debug_get_error() const;
- virtual int debug_get_stack_level_count() const;
- virtual int debug_get_stack_level_line(int p_level) const;
- virtual String debug_get_stack_level_function(int p_level) const;
- virtual String debug_get_stack_level_source(int p_level) const;
- /* TODO */ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
- /* TODO */ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
- /* TODO */ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
- /* TODO */ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { return ""; }
- virtual Vector<StackInfo> debug_get_current_stack_info();
+ String debug_get_error() const override;
+ int debug_get_stack_level_count() const override;
+ int debug_get_stack_level_line(int p_level) const override;
+ String debug_get_stack_level_function(int p_level) const override;
+ String debug_get_stack_level_source(int p_level) const override;
+ /* 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 ""; }
+ Vector<StackInfo> debug_get_current_stack_info() override;
/* PROFILING FUNCTIONS */
- /* TODO */ virtual void profiling_start() {}
- /* TODO */ virtual void profiling_stop() {}
- /* TODO */ virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) { return 0; }
- /* TODO */ virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) { return 0; }
+ /* 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; }
- virtual void frame();
+ void frame() override;
- /* TODO? */ virtual void get_public_functions(List<MethodInfo> *p_functions) const {}
- /* TODO? */ virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const {}
+ /* TODO? */ void get_public_functions(List<MethodInfo> *p_functions) const override {}
+ /* TODO? */ void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {}
- virtual void reload_all_scripts();
- virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload);
+ void reload_all_scripts() override;
+ void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
/* LOADER FUNCTIONS */
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ void get_recognized_extensions(List<String> *p_extensions) const override;
#ifdef TOOLS_ENABLED
- virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col);
- virtual bool overrides_external_editor();
+ Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) override;
+ bool overrides_external_editor() override;
#endif
/* THREAD ATTACHING */
- virtual void thread_enter();
- virtual void thread_exit();
+ void thread_enter() override;
+ void thread_exit() override;
// Don't use these. I'm watching you
- virtual void *alloc_instance_binding_data(Object *p_object);
- virtual void free_instance_binding_data(void *p_data);
- virtual void refcount_incremented_instance_binding(Object *p_object);
- virtual bool refcount_decremented_instance_binding(Object *p_object);
+ void *alloc_instance_binding_data(Object *p_object) override;
+ void free_instance_binding_data(void *p_data) override;
+ void refcount_incremented_instance_binding(Object *p_object) override;
+ bool refcount_decremented_instance_binding(Object *p_object) override;
Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
@@ -525,17 +532,17 @@ public:
class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
+ RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false) override;
+ void get_recognized_extensions(List<String> *p_extensions) const override;
+ bool handles_type(const String &p_type) const override;
+ String get_resource_type(const String &p_path) const override;
};
class ResourceFormatSaverCSharpScript : public ResourceFormatSaver {
public:
- virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
- virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
- virtual bool recognize(const RES &p_resource) const;
+ Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0) override;
+ void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const override;
+ bool recognize(const RES &p_resource) const override;
};
#endif // CSHARP_SCRIPT_H
diff --git a/modules/mono/doc_classes/@C#.xml b/modules/mono/doc_classes/@C#.xml
deleted file mode 100644
index 83a7fbf02c..0000000000
--- a/modules/mono/doc_classes/@C#.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@C#" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index 1eb3404f9e..45a6f991bf 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CSharpScript" inherits="Script" version="4.0">
<brief_description>
+ A script implemented in the C# programming language (Mono-enabled builds only).
</brief_description>
<description>
+ This class represents a C# script. It is the C# equivalent of the [GDScript] class and is only available in Mono-enabled Godot builds.
+ See also [GodotSharp].
</description>
<tutorials>
+ <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link>
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
<return type="Variant">
</return>
<description>
+ Returns a new instance of the script.
</description>
</method>
</methods>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 19a08d2036..417f8ac704 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GodotSharp" inherits="Object" version="4.0">
<brief_description>
+ Bridge between Godot and the Mono runtime (Mono-enabled builds only).
</brief_description>
<description>
+ This class is a bridge between Godot and the Mono runtime. It exposes several low-level operations and is only available in Mono-enabled Godot builds.
+ See also [CSharpScript].
</description>
<tutorials>
</tutorials>
@@ -11,26 +14,30 @@
<return type="void">
</return>
<description>
- Attaches the current thread to the mono runtime.
+ Attaches the current thread to the Mono runtime.
</description>
</method>
<method name="detach_thread">
<return type="void">
</return>
<description>
- Detaches the current thread from the mono runtime.
+ Detaches the current thread from the Mono runtime.
</description>
</method>
<method name="get_domain_id">
<return type="int">
</return>
<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">
</return>
<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">
@@ -39,26 +46,28 @@
<argument index="0" name="domain_id" type="int">
</argument>
<description>
- Returns whether the domain is being finalized.
+ Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_initialized">
<return type="bool">
</return>
<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">
</return>
<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">
</return>
<description>
- Returns whether the scripts domain is loaded.
+ Returns [code]true[/code] if the scripts domain is loaded, [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
new file mode 100644
index 0000000000..56c0cb7703
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
@@ -0,0 +1,16 @@
+
+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
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
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
new file mode 100644
index 0000000000..86a0a4393e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -0,0 +1,35 @@
+<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+
+ <Description>MSBuild .NET Sdk for Godot projects.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>Godot.NET.Sdk</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>4.0.0-dev2</PackageVersion>
+ <PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl>
+ <PackageType>MSBuildSdk</PackageType>
+ <PackageTags>MSBuildSdk</PackageTags>
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <NuspecFile>Godot.NET.Sdk.nuspec</NuspecFile>
+ <GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn>
+ </PropertyGroup>
+
+ <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') ">
+ <PropertyGroup>
+ <NuspecProperties>
+ id=$(PackageId);
+ description=$(Description);
+ authors=$(Authors);
+ version=$(PackageVersion);
+ packagetype=$(PackageType);
+ tags=$(PackageTags);
+ projecturl=$(PackageProjectUrl)
+ </NuspecProperties>
+ </PropertyGroup>
+ </Target>
+</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
new file mode 100644
index 0000000000..5b5cefe80e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
+ <metadata>
+ <id>$id$</id>
+ <version>$version$</version>
+ <description>$description$</description>
+ <authors>$authors$</authors>
+ <owners>$authors$</owners>
+ <projectUrl>$projecturl$</projectUrl>
+ <requireLicenseAcceptance>false</requireLicenseAcceptance>
+ <license type="expression">MIT</license>
+ <licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
+ <tags>$tags$</tags>
+ <packageTypes>
+ <packageType name="$packagetype$" />
+ </packageTypes>
+ <repository url="$projecturl$" />
+ </metadata>
+ <files>
+ <file src="Sdk\**" target="Sdk" />\
+ </files>
+</package>
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
new file mode 100644
index 0000000000..5febcf3175
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -0,0 +1,112 @@
+<Project>
+ <PropertyGroup>
+ <!-- Determines if we should import Microsoft.NET.Sdk, if it wasn't already imported. -->
+ <GodotSdkImportsMicrosoftNetSdk Condition=" '$(UsingMicrosoftNETSdk)' != 'true' ">true</GodotSdkImportsMicrosoftNetSdk>
+
+ <GodotProjectTypeGuid>{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}</GodotProjectTypeGuid>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <Configurations>Debug;ExportDebug;ExportRelease</Configurations>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+
+ <GodotProjectDir Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)</GodotProjectDir>
+ <GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
+ <GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
+
+ <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
+ <BaseOutputPath>$(GodotProjectDir).godot\mono\temp\bin\</BaseOutputPath>
+ <OutputPath>$(GodotProjectDir).godot\mono\temp\bin\$(Configuration)\</OutputPath>
+ <!--
+ Use custom IntermediateOutputPath and BaseIntermediateOutputPath only if it wasn't already set.
+ Otherwise the old values may have already been changed by MSBuild which can cause problems with NuGet.
+ -->
+ <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\$(Configuration)\</IntermediateOutputPath>
+ <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\</BaseIntermediateOutputPath>
+
+ <!-- Do not append the target framework name to the output path. -->
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
+ </PropertyGroup>
+
+ <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
+
+ <PropertyGroup>
+ <EnableDefaultNoneItems>false</EnableDefaultNoneItems>
+ </PropertyGroup>
+
+ <!--
+ The Microsoft.NET.Sdk only understands of the Debug and Release configurations.
+ We need to set the following properties manually for ExportDebug and ExportRelease.
+ -->
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' or '$(Configuration)' == 'ExportDebug' ">
+ <DebugSymbols Condition=" '$(DebugSymbols)' == '' ">true</DebugSymbols>
+ <Optimize Condition=" '$(Optimize)' == '' ">false</Optimize>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'ExportRelease' ">
+ <Optimize Condition=" '$(Optimize)' == '' ">true</Optimize>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <GodotApiConfiguration Condition=" '$(Configuration)' != 'ExportRelease' ">Debug</GodotApiConfiguration>
+ <GodotApiConfiguration Condition=" '$(Configuration)' == 'ExportRelease' ">Release</GodotApiConfiguration>
+ </PropertyGroup>
+
+ <!-- Auto-detect the target Godot platform if it was not specified. -->
+ <PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' ">
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Linux))' ">linuxbsd</GodotTargetPlatform>
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">linuxbsd</GodotTargetPlatform>
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(OSX))' ">osx</GodotTargetPlatform>
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Windows))' ">windows</GodotTargetPlatform>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <GodotRealTIsDouble Condition=" '$(GodotRealTIsDouble)' == '' ">false</GodotRealTIsDouble>
+ </PropertyGroup>
+
+ <!-- Godot DefineConstants. -->
+ <PropertyGroup>
+ <!-- Define constant to identify Godot builds. -->
+ <GodotDefineConstants>GODOT</GodotDefineConstants>
+
+ <!--
+ Define constant to determine the target Godot platform. This includes the
+ recognized platform names and the platform category (PC, MOBILE or WEB).
+ -->
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'windows' ">GODOT_WINDOWS;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'linuxbsd' ">GODOT_LINUXBSD;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'osx' ">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>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'iphone' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'javascript' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
+
+ <GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!-- ExportDebug also defines DEBUG like Debug does. -->
+ <DefineConstants Condition=" '$(Configuration)' == 'ExportDebug' ">$(DefineConstants);DEBUG</DefineConstants>
+ <!-- Debug defines TOOLS to differentiate between Debug and ExportDebug configurations. -->
+ <DefineConstants Condition=" '$(Configuration)' == 'Debug' ">$(DefineConstants);TOOLS</DefineConstants>
+
+ <DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
+ </PropertyGroup>
+
+ <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
new file mode 100644
index 0000000000..f5afd75505
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
@@ -0,0 +1,17 @@
+<Project>
+ <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
+
+ <PropertyGroup>
+ <EnableGodotProjectTypeGuid Condition=" '$(EnableGodotProjectTypeGuid)' == '' ">true</EnableGodotProjectTypeGuid>
+ <ProjectTypeGuids Condition=" '$(EnableGodotProjectTypeGuid)' == 'true' ">$(GodotProjectTypeGuid);$(DefaultProjectTypeGuid)</ProjectTypeGuids>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!--
+ Define constant to determine whether the real_t type in Godot is double precision or not.
+ By default this is false, like the official Godot builds. If someone is using a custom
+ Godot build where real_t is double, they can override the GodotRealTIsDouble property.
+ -->
+ <DefineConstants Condition=" '$(GodotRealTIsDouble)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
+ </PropertyGroup>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index c2549b4ad5..5edf72d63e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -70,13 +70,14 @@ namespace GodotTools.BuildLogger
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
- if (e.ProjectFile.Length > 0)
+ if (!string.IsNullOrEmpty(e.ProjectFile))
line += $" [{e.ProjectFile}]";
WriteLine(line);
string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
- $@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}";
+ $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
+ $"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
issuesStreamWriter.WriteLine(errorLine);
}
@@ -89,8 +90,9 @@ namespace GodotTools.BuildLogger
WriteLine(line);
- string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," +
- $@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}";
+ string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
+ $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
+ $"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
issuesStreamWriter.WriteLine(warningLine);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs
index 85760a3705..e1ccf0454a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs
@@ -19,7 +19,10 @@ namespace GodotTools.Core
}
if (attempt > maxAttempts + 1)
- return;
+ {
+ // Overwrite the oldest one
+ backupPath = backupPathBase;
+ }
File.Copy(filePath, backupPath, overwrite: true);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index 7ab5c5fc59..b217ae4bf7 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Runtime.InteropServices;
namespace GodotTools.Core
{
@@ -14,23 +15,38 @@ namespace GodotTools.Core
if (Path.DirectorySeparatorChar == '\\')
dir = dir.Replace("/", "\\") + "\\";
- Uri fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute);
- Uri relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute);
+ var fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute);
+ var relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute);
- return relRoot.MakeRelativeUri(fullPath).ToString();
+ // MakeRelativeUri converts spaces to %20, hence why we need UnescapeDataString
+ return Uri.UnescapeDataString(relRoot.MakeRelativeUri(fullPath).ToString());
}
public static string NormalizePath(this string path)
{
+ if (string.IsNullOrEmpty(path))
+ return path;
+
bool rooted = path.IsAbsolutePath();
path = path.Replace('\\', '/');
+ path = path[path.Length - 1] == '/' ? path.Substring(0, path.Length - 1) : path;
- string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+ string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
- return rooted ? Path.DirectorySeparatorChar + path : path;
+ if (!rooted)
+ return path;
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ string maybeDrive = parts[0];
+ if (maybeDrive.Length == 2 && maybeDrive[1] == ':')
+ return path; // Already has drive letter
+ }
+
+ return Path.DirectorySeparatorChar + path;
}
private static readonly string DriveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
@@ -42,9 +58,9 @@ namespace GodotTools.Core
path.StartsWith(DriveRoot, StringComparison.Ordinal);
}
- public static string ToSafeDirName(this string dirName, bool allowDirSeparator)
+ public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false)
{
- var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" };
+ var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"};
if (allowDirSeparator)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
index 99a55c471b..4db71500da 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index d069651dd3..0f50c90531 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -19,9 +19,12 @@ namespace GodotTools.IdeMessaging
private readonly string identity;
private string MetaFilePath { get; }
+ private DateTime? metaFileModifiedTime;
private GodotIdeMetadata godotIdeMetadata;
private readonly FileSystemWatcher fsWatcher;
+ public string GodotEditorExecutablePath => godotIdeMetadata.EditorExecutablePath;
+
private readonly IMessageHandler messageHandler;
private Peer peer;
@@ -118,12 +121,12 @@ namespace GodotTools.IdeMessaging
this.messageHandler = messageHandler;
this.logger = logger;
- string projectMetadataDir = Path.Combine(godotProjectDir, ".mono", "metadata");
+ string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata");
MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
// FileSystemWatcher requires an existing directory
- if (!File.Exists(projectMetadataDir))
+ if (!Directory.Exists(projectMetadataDir))
Directory.CreateDirectory(projectMetadataDir);
fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
@@ -142,6 +145,13 @@ namespace GodotTools.IdeMessaging
if (!File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null && metadata != godotIdeMetadata)
@@ -173,6 +183,13 @@ namespace GodotTools.IdeMessaging
if (IsConnected || !File.Exists(MetaFilePath))
return;
+ var lastWriteTime = File.GetLastWriteTime(MetaFilePath);
+
+ if (lastWriteTime == metaFileModifiedTime)
+ return;
+
+ metaFileModifiedTime = lastWriteTime;
+
var metadata = ReadMetadataFile();
if (metadata != null)
@@ -185,7 +202,8 @@ namespace GodotTools.IdeMessaging
private GodotIdeMetadata? ReadMetadataFile()
{
- using (var reader = File.OpenText(MetaFilePath))
+ using (var fileStream = new FileStream(MetaFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ using (var reader = new StreamReader(fileStream))
{
string portStr = reader.ReadLine();
@@ -272,6 +290,7 @@ namespace GodotTools.IdeMessaging
// ReSharper disable once UnusedMember.Global
public async void Start()
{
+ fsWatcher.Created += OnMetaFileChanged;
fsWatcher.Changed += OnMetaFileChanged;
fsWatcher.Deleted += OnMetaFileDeleted;
fsWatcher.EnableRaisingEvents = true;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
index 67815959a6..dad6b9ae7a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
@@ -4,7 +4,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.2</LangVersion>
<PackageId>GodotTools.IdeMessaging</PackageId>
- <Version>1.1.0</Version>
+ <Version>1.1.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<Authors>Godot Engine contributors</Authors>
<Company />
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
index a4e86d6177..10d7e1898e 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
@@ -105,49 +105,45 @@ namespace GodotTools.IdeMessaging
try
{
- try
+ if (msg.Kind == MessageKind.Request)
{
- if (msg.Kind == MessageKind.Request)
- {
- var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
- await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
- }
- else if (msg.Kind == MessageKind.Response)
- {
- ResponseAwaiter responseAwaiter;
+ var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger);
+ await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent));
+ }
+ else if (msg.Kind == MessageKind.Response)
+ {
+ ResponseAwaiter responseAwaiter;
- using (await requestsSem.UseAsync())
+ using (await requestsSem.UseAsync())
+ {
+ if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
{
- if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)
- {
- Logger.LogError($"Received unexpected response: {msg.Id}");
- return;
- }
-
- responseAwaiter = queue.Dequeue();
+ Logger.LogError($"Received unexpected response: {msg.Id}");
+ return;
}
- responseAwaiter.SetResult(msg.Content);
- }
- else
- {
- throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
+ responseAwaiter = queue.Dequeue();
}
+
+ responseAwaiter.SetResult(msg.Content);
}
- catch (Exception e)
+ else
{
- Logger.LogError($"Message handler for '{msg}' failed with exception", e);
+ throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");
}
}
catch (Exception e)
{
- Logger.LogError($"Exception thrown from message handler. Message: {msg}", e);
+ Logger.LogError($"Message handler for '{msg}' failed with exception", e);
}
}
}
catch (Exception e)
{
- Logger.LogError("Unhandled exception in the peer loop", e);
+ if (!IsDisposed || !(e is SocketException || e.InnerException is SocketException))
+ {
+ Logger.LogError("Unhandled exception in the peer loop", e);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
index 1dd4f852e5..e93db9377b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs
@@ -67,6 +67,19 @@ namespace GodotTools.IdeMessaging.Requests
{
}
+ public sealed class StopPlayRequest : Request
+ {
+ public new const string Id = "StopPlay";
+
+ public StopPlayRequest() : base(Id)
+ {
+ }
+ }
+
+ public sealed class StopPlayResponse : Response
+ {
+ }
+
public sealed class DebugPlayRequest : Request
{
public string DebuggerHost { get; set; }
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
new file mode 100644
index 0000000000..5b3ed0b1b7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net472</TargetFramework>
+ <LangVersion>7.2</LangVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
+ <PackageReference Include="EnvDTE" Version="8.0.2" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
new file mode 100644
index 0000000000..ce2b378623
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -0,0 +1,270 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text.RegularExpressions;
+using EnvDTE;
+
+namespace GodotTools.OpenVisualStudio
+{
+ internal static class Program
+ {
+ [DllImport("ole32.dll")]
+ private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot);
+
+ [DllImport("ole32.dll")]
+ private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
+
+ [DllImport("user32.dll")]
+ private static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ private static void ShowHelp()
+ {
+ Console.WriteLine("Opens the file(s) in a Visual Studio instance that is editing the specified solution.");
+ Console.WriteLine("If an existing instance for the solution is not found, a new one is created.");
+ Console.WriteLine();
+ Console.WriteLine("Usage:");
+ Console.WriteLine(@" GodotTools.OpenVisualStudio.exe solution [file[;line[;col]]...]");
+ Console.WriteLine();
+ Console.WriteLine("Lines and columns begin at one. Zero or lower will result in an error.");
+ Console.WriteLine("If a line is specified but a column is not, the line is selected in the text editor.");
+ }
+
+ // STAThread needed, otherwise CoRegisterMessageFilter may return CO_E_NOT_SUPPORTED.
+ [STAThread]
+ private static int Main(string[] args)
+ {
+ if (args.Length == 0 || args[0] == "--help" || args[0] == "-h")
+ {
+ ShowHelp();
+ return 0;
+ }
+
+ string solutionFile = NormalizePath(args[0]);
+
+ var dte = FindInstanceEditingSolution(solutionFile);
+
+ if (dte == null)
+ {
+ // Open a new instance
+
+ var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true);
+ dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+
+ dte.UserControl = true;
+
+ try
+ {
+ dte.Solution.Open(solutionFile);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("Solution.Open: Invalid path or file not found");
+ return 1;
+ }
+
+ dte.MainWindow.Visible = true;
+ }
+
+ MessageFilter.Register();
+
+ try
+ {
+ // Open files
+
+ for (int i = 1; i < args.Length; i++)
+ {
+ // Both the line number and the column begin at one
+
+ string[] fileArgumentParts = args[i].Split(';');
+
+ string filePath = NormalizePath(fileArgumentParts[0]);
+
+ try
+ {
+ dte.ItemOperations.OpenFile(filePath);
+ }
+ catch (ArgumentException)
+ {
+ Console.Error.WriteLine("ItemOperations.OpenFile: Invalid path or file not found");
+ return 1;
+ }
+
+ if (fileArgumentParts.Length > 1)
+ {
+ if (int.TryParse(fileArgumentParts[1], out int line))
+ {
+ var textSelection = (TextSelection)dte.ActiveDocument.Selection;
+
+ if (fileArgumentParts.Length > 2)
+ {
+ if (int.TryParse(fileArgumentParts[2], out int column))
+ {
+ textSelection.MoveToLineAndOffset(line, column);
+ }
+ else
+ {
+ Console.Error.WriteLine("The column part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ else
+ {
+ textSelection.GotoLine(line, Select: true);
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("The line part of the argument must be a valid integer");
+ return 1;
+ }
+ }
+ }
+ }
+ finally
+ {
+ var mainWindow = dte.MainWindow;
+ mainWindow.Activate();
+ SetForegroundWindow(new IntPtr(mainWindow.HWnd));
+
+ MessageFilter.Revoke();
+ }
+
+ return 0;
+ }
+
+ private static DTE FindInstanceEditingSolution(string solutionPath)
+ {
+ if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
+ return null;
+
+ try
+ {
+ pprot.EnumRunning(out IEnumMoniker ppenumMoniker);
+ ppenumMoniker.Reset();
+
+ var moniker = new IMoniker[1];
+
+ while (ppenumMoniker.Next(1, moniker, IntPtr.Zero) == 0)
+ {
+ string ppszDisplayName;
+
+ CreateBindCtx(0, out IBindCtx ppbc);
+
+ try
+ {
+ moniker[0].GetDisplayName(ppbc, null, out ppszDisplayName);
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(ppbc);
+ }
+
+ if (ppszDisplayName == null)
+ continue;
+
+ // The digits after the colon are the process ID
+ if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]"))
+ continue;
+
+ if (pprot.GetObject(moniker[0], out object ppunkObject) == 0)
+ {
+ if (ppunkObject is DTE dte && dte.Solution.FullName.Length > 0)
+ {
+ if (NormalizePath(dte.Solution.FullName) == solutionPath)
+ return dte;
+ }
+ }
+ }
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(pprot);
+ }
+
+ return null;
+ }
+
+ static string NormalizePath(string path)
+ {
+ return new Uri(Path.GetFullPath(path)).LocalPath
+ .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
+ .ToUpperInvariant();
+ }
+
+ #region MessageFilter. See: http: //msdn.microsoft.com/en-us/library/ms228772.aspx
+
+ private class MessageFilter : IOleMessageFilter
+ {
+ // Class containing the IOleMessageFilter
+ // thread error-handling functions
+
+ private static IOleMessageFilter _oldFilter;
+
+ // Start the filter
+ public static void Register()
+ {
+ IOleMessageFilter newFilter = new MessageFilter();
+ int ret = CoRegisterMessageFilter(newFilter, out _oldFilter);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ // Done with the filter, close it
+ public static void Revoke()
+ {
+ int ret = CoRegisterMessageFilter(_oldFilter, out _);
+ if (ret != 0)
+ Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}");
+ }
+
+ //
+ // IOleMessageFilter functions
+ // Handle incoming thread requests
+ int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
+ {
+ // Return the flag SERVERCALL_ISHANDLED
+ return 0;
+ }
+
+ // Thread call was rejected, so try again.
+ int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
+ {
+ if (dwRejectType == 2)
+ // flag = SERVERCALL_RETRYLATER
+ {
+ // Retry the thread call immediately if return >= 0 & < 100
+ return 99;
+ }
+
+ // Too busy; cancel call
+ return -1;
+ }
+
+ int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
+ {
+ // Return the flag PENDINGMSG_WAITDEFPROCESS
+ return 2;
+ }
+
+ // Implement the IOleMessageFilter interface
+ [DllImport("ole32.dll")]
+ private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
+ }
+
+ [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ private interface IOleMessageFilter
+ {
+ [PreserveSig]
+ int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
+
+ [PreserveSig]
+ int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
+
+ [PreserveSig]
+ int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
+ }
+
+ #endregion
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
index 6f318aab4a..cc0da44a13 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
@@ -2,6 +2,7 @@ using GodotTools.Core;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text;
using System.Text.RegularExpressions;
namespace GodotTools.ProjectEditor
@@ -88,7 +89,7 @@ namespace GodotTools.ProjectEditor
string solutionPath = Path.Combine(DirectoryPath, Name + ".sln");
string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
- File.WriteAllText(solutionPath, content);
+ File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM
}
public DotNetSolution(string name)
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index 9cb50014b0..e4d6b2e010 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -11,13 +11,21 @@
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.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>
- <!--
- 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.
- -->
<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/IdentifierUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
index f93eb9a1fa..ed77076df3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
@@ -22,6 +22,37 @@ namespace GodotTools.ProjectEditor
return string.Join(".", identifiers);
}
+ /// <summary>
+ /// Skips invalid identifier characters including decimal digit numbers at the start of the identifier.
+ /// </summary>
+ private static void SkipInvalidCharacters(string source, int startIndex, StringBuilder outputBuilder)
+ {
+ for (int i = startIndex; i < source.Length; i++)
+ {
+ char @char = source[i];
+
+ switch (char.GetUnicodeCategory(@char))
+ {
+ case UnicodeCategory.UppercaseLetter:
+ case UnicodeCategory.LowercaseLetter:
+ case UnicodeCategory.TitlecaseLetter:
+ case UnicodeCategory.ModifierLetter:
+ case UnicodeCategory.LetterNumber:
+ case UnicodeCategory.OtherLetter:
+ outputBuilder.Append(@char);
+ break;
+ case UnicodeCategory.NonSpacingMark:
+ case UnicodeCategory.SpacingCombiningMark:
+ case UnicodeCategory.ConnectorPunctuation:
+ case UnicodeCategory.DecimalDigitNumber:
+ // Identifiers may start with underscore
+ if (outputBuilder.Length > startIndex || @char == '_')
+ outputBuilder.Append(@char);
+ break;
+ }
+ }
+ }
+
public static string SanitizeIdentifier(string identifier, bool allowEmpty)
{
if (string.IsNullOrEmpty(identifier))
@@ -44,30 +75,7 @@ namespace GodotTools.ProjectEditor
startIndex += 1;
}
- for (int i = startIndex; i < identifier.Length; i++)
- {
- char @char = identifier[i];
-
- switch (Char.GetUnicodeCategory(@char))
- {
- case UnicodeCategory.UppercaseLetter:
- case UnicodeCategory.LowercaseLetter:
- case UnicodeCategory.TitlecaseLetter:
- case UnicodeCategory.ModifierLetter:
- case UnicodeCategory.LetterNumber:
- case UnicodeCategory.OtherLetter:
- identifierBuilder.Append(@char);
- break;
- case UnicodeCategory.NonSpacingMark:
- case UnicodeCategory.SpacingCombiningMark:
- case UnicodeCategory.ConnectorPunctuation:
- case UnicodeCategory.DecimalDigitNumber:
- // Identifiers may start with underscore
- if (identifierBuilder.Length > startIndex || @char == '_')
- identifierBuilder.Append(@char);
- break;
- }
- }
+ SkipInvalidCharacters(identifier, startIndex, identifierBuilder);
if (identifierBuilder.Length == startIndex)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
deleted file mode 100644
index 704f2ec194..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using GodotTools.Core;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Microsoft.Build.Construction;
-using Microsoft.Build.Globbing;
-
-namespace GodotTools.ProjectEditor
-{
- public static class ProjectExtensions
- {
- public static ProjectItemElement FindItemOrNull(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
- {
- string normalizedInclude = include.NormalizePath();
-
- foreach (var itemGroup in root.ItemGroups)
- {
- if (noCondition && itemGroup.Condition.Length != 0)
- continue;
-
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- //var glob = Glob.Parse(item.Include.NormalizePath(), globOptions);
- var glob = MSBuildGlob.Parse(item.Include.NormalizePath());
-
- if (glob.IsMatch(normalizedInclude))
- return item;
- }
- }
-
- return null;
- }
- public static ProjectItemElement FindItemOrNullAbs(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
- {
- string normalizedInclude = Path.GetFullPath(include).NormalizePath();
-
- foreach (var itemGroup in root.ItemGroups)
- {
- if (noCondition && itemGroup.Condition.Length != 0)
- continue;
-
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- var glob = MSBuildGlob.Parse(Path.GetFullPath(item.Include).NormalizePath());
-
- if (glob.IsMatch(normalizedInclude))
- return item;
- }
- }
-
- return null;
- }
-
- public static IEnumerable<ProjectItemElement> FindAllItemsInFolder(this ProjectRootElement root, string itemType, string folder)
- {
- string absFolderNormalizedWithSep = Path.GetFullPath(folder).NormalizePath() + Path.DirectorySeparatorChar;
-
- foreach (var itemGroup in root.ItemGroups)
- {
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
-
- if (absPathNormalized.StartsWith(absFolderNormalizedWithSep))
- yield return item;
- }
- }
- }
-
- public static bool HasItem(this ProjectRootElement root, string itemType, string include, bool noCondition = false)
- {
- return root.FindItemOrNull(itemType, include, noCondition) != null;
- }
-
- public static bool AddItemChecked(this ProjectRootElement root, string itemType, string include)
- {
- if (!root.HasItem(itemType, include, noCondition: true))
- {
- root.AddItem(itemType, include);
- return true;
- }
-
- return false;
- }
-
- public static bool RemoveItemChecked(this ProjectRootElement root, string itemType, string include)
- {
- var item = root.FindItemOrNullAbs(itemType, include);
- if (item != null)
- {
- item.Parent.RemoveChild(item);
- return true;
- }
-
- return false;
- }
-
- public static Guid GetGuid(this ProjectRootElement root)
- {
- foreach (var property in root.Properties)
- {
- if (property.Name == "ProjectGuid")
- return Guid.Parse(property.Value);
- }
-
- return Guid.Empty;
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index fb2beb6995..01d7c99662 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -1,168 +1,51 @@
-using GodotTools.Core;
using System;
-using System.Collections.Generic;
using System.IO;
-using System.Reflection;
+using System.Text;
using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
namespace GodotTools.ProjectEditor
{
public static class ProjectGenerator
{
- private const string CoreApiProjectName = "GodotSharp";
- private const string EditorApiProjectName = "GodotSharpEditor";
+ public const string GodotSdkVersionToUse = "4.0.0-dev2";
- public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
- {
- string path = Path.Combine(dir, name + ".csproj");
-
- ProjectPropertyGroupElement mainGroup;
- var root = CreateLibraryProject(name, "Debug", out mainGroup);
-
- mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
- mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
- mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
- mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'ExportRelease' ";
- mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'ExportRelease' ";
-
- var debugGroup = root.AddPropertyGroup();
- debugGroup.Condition = " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ";
- debugGroup.AddProperty("DebugSymbols", "true");
- debugGroup.AddProperty("DebugType", "portable");
- debugGroup.AddProperty("Optimize", "false");
- debugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;TOOLS;");
- debugGroup.AddProperty("ErrorReport", "prompt");
- debugGroup.AddProperty("WarningLevel", "4");
- debugGroup.AddProperty("ConsolePause", "false");
-
- var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
- coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll"));
- coreApiRef.AddMetadata("Private", "False");
-
- var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
- editorApiRef.Condition = " '$(Configuration)' == 'Debug' ";
- editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll"));
- editorApiRef.AddMetadata("Private", "False");
+ public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}";
- GenAssemblyInfoFile(root, dir, name);
-
- foreach (var item in compileItems)
- {
- root.AddItem("Compile", item.RelativeToPath(dir).Replace("/", "\\"));
- }
-
- root.Save(path);
-
- return root.GetGuid().ToString().ToUpper();
- }
-
- private static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null)
+ public static ProjectRootElement GenGameProject(string name)
{
- string propertiesDir = Path.Combine(dir, "Properties");
- if (!Directory.Exists(propertiesDir))
- Directory.CreateDirectory(propertiesDir);
-
- string usingDirectivesText = string.Empty;
-
- if (usingDirectives != null)
- {
- foreach (var usingDirective in usingDirectives)
- usingDirectivesText += "\nusing " + usingDirective + ";";
- }
+ if (name.Length == 0)
+ throw new ArgumentException("Project name is empty", nameof(name));
- string assemblyLinesText = string.Empty;
+ var root = ProjectRootElement.Create(NewProjectFileOptions.None);
- if (assemblyLines != null)
- assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
+ root.Sdk = GodotSdkAttrValue;
- string content = string.Format(AssemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
+ var mainGroup = root.AddPropertyGroup();
+ mainGroup.AddProperty("TargetFramework", "netstandard2.1");
- string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs");
+ string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
- File.WriteAllText(assemblyInfoFile, content);
+ // If the name is not a valid namespace, manually set RootNamespace to a sanitized one.
+ if (sanitizedName != name)
+ mainGroup.AddProperty("RootNamespace", sanitizedName);
- root.AddItem("Compile", assemblyInfoFile.RelativeToPath(dir).Replace("/", "\\"));
+ return root;
}
- public static ProjectRootElement CreateLibraryProject(string name, string defaultConfig, out ProjectPropertyGroupElement mainGroup)
+ public static string GenAndSaveGameProject(string dir, string name)
{
- if (string.IsNullOrEmpty(name))
- throw new ArgumentException($"{nameof(name)} cannot be empty", nameof(name));
-
- var root = ProjectRootElement.Create();
- root.DefaultTargets = "Build";
-
- mainGroup = root.AddPropertyGroup();
- mainGroup.AddProperty("Configuration", defaultConfig).Condition = " '$(Configuration)' == '' ";
- mainGroup.AddProperty("Platform", "AnyCPU").Condition = " '$(Platform)' == '' ";
- mainGroup.AddProperty("ProjectGuid", "{" + Guid.NewGuid().ToString().ToUpper() + "}");
- mainGroup.AddProperty("OutputType", "Library");
- mainGroup.AddProperty("OutputPath", Path.Combine("bin", "$(Configuration)"));
- mainGroup.AddProperty("RootNamespace", IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true));
- mainGroup.AddProperty("AssemblyName", name);
- mainGroup.AddProperty("TargetFrameworkVersion", "v4.7");
- mainGroup.AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
-
- var exportDebugGroup = root.AddPropertyGroup();
- exportDebugGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportDebug|AnyCPU' ";
- exportDebugGroup.AddProperty("DebugSymbols", "true");
- exportDebugGroup.AddProperty("DebugType", "portable");
- exportDebugGroup.AddProperty("Optimize", "false");
- exportDebugGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;DEBUG;");
- exportDebugGroup.AddProperty("ErrorReport", "prompt");
- exportDebugGroup.AddProperty("WarningLevel", "4");
- exportDebugGroup.AddProperty("ConsolePause", "false");
+ if (name.Length == 0)
+ throw new ArgumentException("Project name is empty", nameof(name));
- var exportReleaseGroup = root.AddPropertyGroup();
- exportReleaseGroup.Condition = " '$(Configuration)|$(Platform)' == 'ExportRelease|AnyCPU' ";
- exportReleaseGroup.AddProperty("DebugType", "portable");
- exportReleaseGroup.AddProperty("Optimize", "true");
- exportReleaseGroup.AddProperty("DefineConstants", "$(GodotDefineConstants);GODOT;");
- exportReleaseGroup.AddProperty("ErrorReport", "prompt");
- exportReleaseGroup.AddProperty("WarningLevel", "4");
- exportReleaseGroup.AddProperty("ConsolePause", "false");
-
- // References
- var referenceGroup = root.AddItemGroup();
- referenceGroup.AddItem("Reference", "System");
- var frameworkRefAssembliesItem = referenceGroup.AddItem("PackageReference", "Microsoft.NETFramework.ReferenceAssemblies");
+ string path = Path.Combine(dir, name + ".csproj");
- // Use metadata (child nodes) instead of attributes for the PackageReference.
- // This is for compatibility with 3.2, where GodotTools uses an old Microsoft.Build.
- frameworkRefAssembliesItem.AddMetadata("Version", "1.0.0");
- frameworkRefAssembliesItem.AddMetadata("PrivateAssets", "All");
+ var root = GenGameProject(name);
- root.AddImport(Path.Combine("$(MSBuildBinPath)", "Microsoft.CSharp.targets").Replace("/", "\\"));
+ // Save (without BOM)
+ root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
- return root;
+ return Guid.NewGuid().ToString().ToUpper();
}
-
- private const string AssemblyInfoTemplate =
- @"using System.Reflection;{0}
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle(""{1}"")]
-[assembly: AssemblyDescription("""")]
-[assembly: AssemblyConfiguration("""")]
-[assembly: AssemblyCompany("""")]
-[assembly: AssemblyProduct("""")]
-[assembly: AssemblyCopyright("""")]
-[assembly: AssemblyTrademark("""")]
-[assembly: AssemblyCulture("""")]
-
-// The assembly version has the format ""{{Major}}.{{Minor}}.{{Build}}.{{Revision}}"".
-// The form ""{{Major}}.{{Minor}}.*"" will automatically update the build and revision,
-// and ""{{Major}}.{{Minor}}.{{Build}}.*"" will update just the revision.
-
-[assembly: AssemblyVersion(""1.0.*"")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("""")]
-{2}";
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 069a1edaa3..4e2c0f17cc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -1,9 +1,9 @@
+using System;
using GodotTools.Core;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Reflection;
using Microsoft.Build.Construction;
using Microsoft.Build.Globbing;
@@ -11,7 +11,7 @@ namespace GodotTools.ProjectEditor
{
public sealed class MSBuildProject
{
- public ProjectRootElement Root { get; }
+ internal ProjectRootElement Root { get; set; }
public bool HasUnsavedChanges { get; set; }
@@ -31,91 +31,7 @@ namespace GodotTools.ProjectEditor
return root != null ? new MSBuildProject(root) : null;
}
- public static void AddItemToProjectChecked(string projectPath, string itemType, string include)
- {
- var dir = Directory.GetParent(projectPath).FullName;
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var normalizedInclude = include.RelativeToPath(dir).Replace("/", "\\");
-
- if (root.AddItemChecked(itemType, normalizedInclude))
- root.Save();
- }
-
- public static void RenameItemInProjectChecked(string projectPath, string itemType, string oldInclude, string newInclude)
- {
- var dir = Directory.GetParent(projectPath).FullName;
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var normalizedOldInclude = oldInclude.NormalizePath();
- var normalizedNewInclude = newInclude.NormalizePath();
-
- var item = root.FindItemOrNullAbs(itemType, normalizedOldInclude);
-
- if (item == null)
- return;
-
- item.Include = normalizedNewInclude.RelativeToPath(dir).Replace("/", "\\");
- root.Save();
- }
-
- public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include)
- {
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var normalizedInclude = include.NormalizePath();
-
- if (root.RemoveItemChecked(itemType, normalizedInclude))
- root.Save();
- }
-
- public static void RenameItemsToNewFolderInProjectChecked(string projectPath, string itemType, string oldFolder, string newFolder)
- {
- var dir = Directory.GetParent(projectPath).FullName;
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- bool dirty = false;
-
- var oldFolderNormalized = oldFolder.NormalizePath();
- var newFolderNormalized = newFolder.NormalizePath();
- string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath();
- string absNewFolderNormalized = Path.GetFullPath(newFolderNormalized).NormalizePath();
-
- foreach (var item in root.FindAllItemsInFolder(itemType, oldFolderNormalized))
- {
- string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
- string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length);
- item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\");
- dirty = true;
- }
-
- if (dirty)
- root.Save();
- }
-
- public static void RemoveItemsInFolderFromProjectChecked(string projectPath, string itemType, string folder)
- {
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- var folderNormalized = folder.NormalizePath();
-
- var itemsToRemove = root.FindAllItemsInFolder(itemType, folderNormalized).ToList();
-
- if (itemsToRemove.Count > 0)
- {
- foreach (var item in itemsToRemove)
- item.Parent.RemoveChild(item);
-
- root.Save();
- }
- }
-
- private static string[] GetAllFilesRecursive(string rootDirectory, string mask)
+ private static List<string> GetAllFilesRecursive(string rootDirectory, string mask)
{
string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories);
@@ -125,247 +41,58 @@ namespace GodotTools.ProjectEditor
files[i] = files[i].RelativeToPath(rootDirectory);
}
- return files;
+ return new List<string>(files);
}
- public static string[] GetIncludeFiles(string projectPath, string itemType)
+ // NOTE: Assumes auto-including items. Only used by the scripts metadata generator, which will be replaced with source generators in the future.
+ public static IEnumerable<string> GetIncludeFiles(string projectPath, string itemType)
{
- var result = new List<string>();
- var existingFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
+ var excluded = new List<string>();
+ var includedFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
var root = ProjectRootElement.Open(projectPath);
Debug.Assert(root != null);
- foreach (var itemGroup in root.ItemGroups)
+ foreach (var item in root.Items)
{
- if (itemGroup.Condition.Length != 0)
+ if (string.IsNullOrEmpty(item.Condition))
continue;
- foreach (var item in itemGroup.Items)
- {
- if (item.ItemType != itemType)
- continue;
-
- string normalizedInclude = item.Include.NormalizePath();
-
- var glob = MSBuildGlob.Parse(normalizedInclude);
-
- // TODO Check somehow if path has no blob to avoid the following loop...
-
- foreach (var existingFile in existingFiles)
- {
- if (glob.IsMatch(existingFile))
- {
- result.Add(existingFile);
- }
- }
- }
- }
-
- return result.ToArray();
- }
-
- /// Simple function to make sure the Api assembly references are configured correctly
- public static void FixApiHintPath(MSBuildProject project)
- {
- var root = project.Root;
-
- void AddPropertyIfNotPresent(string name, string condition, string value)
- {
- if (root.PropertyGroups
- .Any(g => (string.IsNullOrEmpty(g.Condition) || g.Condition.Trim() == condition) &&
- g.Properties
- .Any(p => p.Name == name &&
- p.Value == value &&
- (p.Condition.Trim() == condition || g.Condition.Trim() == condition))))
- {
- return;
- }
-
- root.AddProperty(name, value).Condition = " " + condition + " ";
- project.HasUnsavedChanges = true;
- }
-
- AddPropertyIfNotPresent(name: "ApiConfiguration",
- condition: "'$(Configuration)' != 'ExportRelease'",
- value: "Debug");
- AddPropertyIfNotPresent(name: "ApiConfiguration",
- condition: "'$(Configuration)' == 'ExportRelease'",
- value: "Release");
-
- void SetReferenceHintPath(string referenceName, string condition, string hintPath)
- {
- foreach (var itemGroup in root.ItemGroups.Where(g =>
- g.Condition.Trim() == string.Empty || g.Condition.Trim() == condition))
- {
- var references = itemGroup.Items.Where(item =>
- item.ItemType == "Reference" &&
- item.Include == referenceName &&
- (item.Condition.Trim() == condition || itemGroup.Condition.Trim() == condition));
-
- var referencesWithHintPath = references.Where(reference =>
- reference.Metadata.Any(m => m.Name == "HintPath"));
-
- if (referencesWithHintPath.Any(reference => reference.Metadata
- .Any(m => m.Name == "HintPath" && m.Value == hintPath)))
- {
- // Found a Reference item with the right HintPath
- return;
- }
-
- var referenceWithHintPath = referencesWithHintPath.FirstOrDefault();
- if (referenceWithHintPath != null)
- {
- // Found a Reference item with a wrong HintPath
- foreach (var metadata in referenceWithHintPath.Metadata.ToList()
- .Where(m => m.Name == "HintPath"))
- {
- // Safe to remove as we duplicate with ToList() to loop
- referenceWithHintPath.RemoveChild(metadata);
- }
-
- referenceWithHintPath.AddMetadata("HintPath", hintPath);
- project.HasUnsavedChanges = true;
- return;
- }
+ if (item.ItemType != itemType)
+ continue;
- var referenceWithoutHintPath = references.FirstOrDefault();
- if (referenceWithoutHintPath != null)
- {
- // Found a Reference item without a HintPath
- referenceWithoutHintPath.AddMetadata("HintPath", hintPath);
- project.HasUnsavedChanges = true;
- return;
- }
- }
+ string normalizedRemove = item.Remove.NormalizePath();
- // Found no Reference item at all. Add it.
- root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
- project.HasUnsavedChanges = true;
+ var glob = MSBuildGlob.Parse(normalizedRemove);
+ excluded.AddRange(includedFiles.Where(includedFile => glob.IsMatch(includedFile)));
}
- const string coreProjectName = "GodotSharp";
- const string editorProjectName = "GodotSharpEditor";
+ includedFiles.RemoveAll(f => excluded.Contains(f));
- const string coreCondition = "";
- const string editorCondition = "'$(Configuration)' == 'Debug'";
-
- var coreHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{coreProjectName}.dll";
- var editorHintPath = $"$(ProjectDir)/.mono/assemblies/$(ApiConfiguration)/{editorProjectName}.dll";
-
- SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath);
- SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath);
+ return includedFiles;
}
- public static void MigrateFromOldConfigNames(MSBuildProject project)
+ public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
{
- var root = project.Root;
+ var origRoot = project.Root;
- bool hasGodotProjectGeneratorVersion = false;
- bool foundOldConfiguration = false;
-
- foreach (var propertyGroup in root.PropertyGroups.Where(g => string.IsNullOrEmpty(g.Condition)))
- {
- if (!hasGodotProjectGeneratorVersion && propertyGroup.Properties.Any(p => p.Name == "GodotProjectGeneratorVersion"))
- hasGodotProjectGeneratorVersion = true;
-
- foreach (var configItem in propertyGroup.Properties
- .Where(p => p.Condition.Trim() == "'$(Configuration)' == ''" && p.Value == "Tools"))
- {
- configItem.Value = "Debug";
- foundOldConfiguration = true;
- project.HasUnsavedChanges = true;
- }
- }
-
- if (!hasGodotProjectGeneratorVersion)
- {
- root.PropertyGroups.First(g => string.IsNullOrEmpty(g.Condition))?
- .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
- project.HasUnsavedChanges = true;
- }
-
- if (!foundOldConfiguration)
- {
- var toolsConditions = new[]
- {
- "'$(Configuration)|$(Platform)' == 'Tools|AnyCPU'",
- "'$(Configuration)|$(Platform)' != 'Tools|AnyCPU'",
- "'$(Configuration)' == 'Tools'",
- "'$(Configuration)' != 'Tools'"
- };
-
- foundOldConfiguration = root.PropertyGroups
- .Any(g => toolsConditions.Any(c => c == g.Condition.Trim()));
- }
-
- if (foundOldConfiguration)
- {
- void MigrateConfigurationConditions(string oldConfiguration, string newConfiguration)
- {
- void MigrateConditions(string oldCondition, string newCondition)
- {
- foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
- {
- propertyGroup.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
-
- foreach (var propertyGroup in root.PropertyGroups)
- {
- foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
- {
- prop.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
- }
-
- foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
- {
- itemGroup.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
-
- foreach (var itemGroup in root.ItemGroups)
- {
- foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
- {
- item.Condition = " " + newCondition + " ";
- project.HasUnsavedChanges = true;
- }
- }
- }
-
- foreach (var op in new[] {"==", "!="})
- {
- MigrateConditions($"'$(Configuration)|$(Platform)' {op} '{oldConfiguration}|AnyCPU'", $"'$(Configuration)|$(Platform)' {op} '{newConfiguration}|AnyCPU'");
- MigrateConditions($"'$(Configuration)' {op} '{oldConfiguration}'", $"'$(Configuration)' {op} '{newConfiguration}'");
- }
- }
+ if (!string.IsNullOrEmpty(origRoot.Sdk))
+ return;
- MigrateConfigurationConditions("Debug", "ExportDebug");
- MigrateConfigurationConditions("Release", "ExportRelease");
- MigrateConfigurationConditions("Tools", "Debug"); // Must be last
- }
+ project.Root = ProjectGenerator.GenGameProject(projectName);
+ project.Root.FullPath = origRoot.FullPath;
+ project.HasUnsavedChanges = true;
}
- public static void EnsureHasNugetNetFrameworkRefAssemblies(MSBuildProject project)
+ public static void EnsureGodotSdkIsUpToDate(MSBuildProject project)
{
var root = project.Root;
+ string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
- bool found = root.ItemGroups.Any(g => string.IsNullOrEmpty(g.Condition) && g.Items.Any(
- item => item.ItemType == "PackageReference" && item.Include == "Microsoft.NETFramework.ReferenceAssemblies"));
-
- if (found)
+ if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
return;
- var frameworkRefAssembliesItem = root.AddItem("PackageReference", "Microsoft.NETFramework.ReferenceAssemblies");
-
- // Use metadata (child nodes) instead of attributes for the PackageReference.
- // This is for compatibility with 3.2, where GodotTools uses an old Microsoft.Build.
- frameworkRefAssembliesItem.AddMetadata("Version", "1.0.0");
- frameworkRefAssembliesItem.AddMetadata("PrivateAssets", "All");
-
+ root.Sdk = godotSdkAttrValue;
project.HasUnsavedChanges = true;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index f6147eb5bb..ba5379e562 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.BuildLogger", "G
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj", "{92600954-25F0-4291-8E11-1FEE9FC4BE20}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -37,5 +39,9 @@ Global
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
index 3cf495f025..3ab669a9f3 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
@@ -20,52 +20,54 @@ namespace GodotTools
private ItemList buildTabsList;
private TabContainer buildTabs;
- private ToolButton warningsBtn;
- private ToolButton errorsBtn;
+ private Button warningsBtn;
+ private Button errorsBtn;
private Button viewLogBtn;
- private void _UpdateBuildTabsList()
+ private void _UpdateBuildTab(int index, int? currentTab)
{
- buildTabsList.Clear();
+ var tab = (BuildTab)buildTabs.GetChild(index);
- int currentTab = buildTabs.CurrentTab;
+ string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
+ itemName += " [" + tab.BuildInfo.Configuration + "]";
- bool noCurrentTab = currentTab < 0 || currentTab >= buildTabs.GetTabCount();
+ buildTabsList.AddItem(itemName, tab.IconTexture);
- for (int i = 0; i < buildTabs.GetChildCount(); i++)
- {
- var tab = (BuildTab)buildTabs.GetChild(i);
+ string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
+ itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
+ itemTooltip += "\nStatus: ";
- if (tab == null)
- continue;
+ if (tab.BuildExited)
+ itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
+ else
+ itemTooltip += "Running";
- string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
- itemName += " [" + tab.BuildInfo.Configuration + "]";
+ if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
+ itemTooltip += $"\nErrors: {tab.ErrorCount}";
- buildTabsList.AddItem(itemName, tab.IconTexture);
+ itemTooltip += $"\nWarnings: {tab.WarningCount}";
- string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
- itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
- itemTooltip += "\nStatus: ";
+ buildTabsList.SetItemTooltip(index, itemTooltip);
- if (tab.BuildExited)
- itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
- else
- itemTooltip += "Running";
+ // If this tab was already selected before the changes or if no tab was selected
+ if (currentTab == null || currentTab == index)
+ {
+ buildTabsList.Select(index);
+ _BuildTabsItemSelected(index);
+ }
+ }
- if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
- itemTooltip += $"\nErrors: {tab.ErrorCount}";
+ private void _UpdateBuildTabsList()
+ {
+ buildTabsList.Clear();
- itemTooltip += $"\nWarnings: {tab.WarningCount}";
+ int? currentTab = buildTabs.CurrentTab;
- buildTabsList.SetItemTooltip(i, itemTooltip);
+ if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
+ currentTab = null;
- if (noCurrentTab || currentTab == i)
- {
- buildTabsList.Select(i);
- _BuildTabsItemSelected(i);
- }
- }
+ for (int i = 0; i < buildTabs.GetChildCount(); i++)
+ _UpdateBuildTab(i, currentTab);
}
public BuildTab GetBuildTabFor(BuildInfo buildInfo)
@@ -160,13 +162,7 @@ namespace GodotTools
}
}
- var godotDefines = new[]
- {
- OS.GetName(),
- Internal.GodotIs32Bits() ? "32" : "64"
- };
-
- bool buildSuccess = BuildManager.BuildProjectBlocking("Debug", godotDefines);
+ bool buildSuccess = BuildManager.BuildProjectBlocking("Debug");
if (!buildSuccess)
return;
@@ -272,7 +268,7 @@ namespace GodotTools
};
panelTabs.AddChild(panelBuildsTab);
- var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
+ var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
panelBuildsTab.AddChild(toolBarHBox);
var buildProjectBtn = new Button
@@ -285,7 +281,7 @@ namespace GodotTools
toolBarHBox.AddSpacer(begin: false);
- warningsBtn = new ToolButton
+ warningsBtn = new Button
{
Text = "Warnings".TTR(),
ToggleMode = true,
@@ -296,7 +292,7 @@ namespace GodotTools
warningsBtn.Toggled += _WarningsToggled;
toolBarHBox.AddChild(warningsBtn);
- errorsBtn = new ToolButton
+ errorsBtn = new Button
{
Text = "Errors".TTR(),
ToggleMode = true,
@@ -325,7 +321,7 @@ namespace GodotTools
};
panelBuildsTab.AddChild(hsc);
- buildTabsList = new ItemList { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
+ buildTabsList = new ItemList {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
buildTabsList.ItemSelected += _BuildTabsItemSelected;
buildTabsList.NothingSelected += _BuildTabsNothingSelected;
hsc.AddChild(buildTabsList);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index e55558c100..d9862ae361 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -47,19 +47,14 @@ namespace GodotTools.Build
private static bool PrintBuildOutput =>
(bool)EditorSettings.GetSetting("mono/builds/print_build_output");
- private static Process LaunchBuild(string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
+ private static Process LaunchBuild(BuildInfo buildInfo)
{
(string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
if (msbuildPath == null)
throw new FileNotFoundException("Cannot find the MSBuild executable.");
- var customPropertiesList = new List<string>();
-
- if (customProperties != null)
- customPropertiesList.AddRange(customProperties);
-
- string compilerArgs = BuildArguments(buildTool, solution, targets, config, loggerOutputDir, customPropertiesList);
+ string compilerArgs = BuildArguments(buildTool, buildInfo);
var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
@@ -100,19 +95,7 @@ namespace GodotTools.Build
public static int Build(BuildInfo buildInfo)
{
- return Build(buildInfo.Solution, buildInfo.Targets, buildInfo.Configuration,
- buildInfo.LogsDirPath, buildInfo.CustomProperties);
- }
-
- public static Task<int> BuildAsync(BuildInfo buildInfo)
- {
- return BuildAsync(buildInfo.Solution, buildInfo.Targets, buildInfo.Configuration,
- buildInfo.LogsDirPath, buildInfo.CustomProperties);
- }
-
- public static int Build(string solution, string[] targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
- {
- using (var process = LaunchBuild(solution, targets, config, loggerOutputDir, customProperties))
+ using (var process = LaunchBuild(buildInfo))
{
process.WaitForExit();
@@ -120,9 +103,9 @@ namespace GodotTools.Build
}
}
- public static async Task<int> BuildAsync(string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
+ public static async Task<int> BuildAsync(BuildInfo buildInfo)
{
- using (var process = LaunchBuild(solution, targets, config, loggerOutputDir, customProperties))
+ using (var process = LaunchBuild(buildInfo))
{
await process.WaitForExitAsync();
@@ -130,17 +113,23 @@ namespace GodotTools.Build
}
}
- private static string BuildArguments(BuildTool buildTool, string solution, IEnumerable<string> targets, string config, string loggerOutputDir, IEnumerable<string> customProperties)
+ private static string BuildArguments(BuildTool buildTool, BuildInfo buildInfo)
{
string arguments = string.Empty;
if (buildTool == BuildTool.DotnetCli)
- arguments += "msbuild "; // `dotnet msbuild` command
+ arguments += "msbuild"; // `dotnet msbuild` command
+
+ arguments += $@" ""{buildInfo.Solution}""";
+
+ if (buildInfo.Restore)
+ arguments += " /restore";
- arguments += $@"""{solution}"" /v:normal /t:{string.Join(",", targets)} ""/p:{"Configuration=" + config}"" " +
- $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}""";
+ arguments += $@" /t:{string.Join(",", buildInfo.Targets)} " +
+ $@"""/p:{"Configuration=" + buildInfo.Configuration}"" /v:normal " +
+ $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{buildInfo.LogsDirPath}""";
- foreach (string customProperty in customProperties)
+ foreach (string customProperty in buildInfo.CustomProperties)
{
arguments += " /p:" + customProperty;
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
index a1a69334e3..837c8adddb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
@@ -1,6 +1,6 @@
namespace GodotTools.Build
{
- public enum BuildTool
+ public enum BuildTool : long
{
MsBuildMono,
MsBuildVs,
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index f36e581a5f..7bfba779fb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -161,8 +161,21 @@ namespace GodotTools.Build
// Try to find 15.0 with vswhere
- string vsWherePath = Environment.GetEnvironmentVariable(Internal.GodotIs32Bits() ? "ProgramFiles" : "ProgramFiles(x86)");
- vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
+ var envNames = Internal.GodotIs32Bits() ? new[] { "ProgramFiles", "ProgramW6432" } : 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"};
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
index cca0983c01..ab090c46e7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
@@ -12,6 +12,7 @@ namespace GodotTools
public string Solution { get; }
public string[] Targets { get; }
public string Configuration { get; }
+ public bool Restore { get; }
public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization
public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
@@ -39,11 +40,12 @@ namespace GodotTools
{
}
- public BuildInfo(string solution, string[] targets, string configuration)
+ public BuildInfo(string solution, string[] targets, string configuration, bool restore)
{
Solution = solution;
Targets = targets;
Configuration = configuration;
+ Restore = restore;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
index 598787ba03..ff7ce97c47 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
@@ -6,6 +6,7 @@ using GodotTools.Build;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
using GodotTools.Utils;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
@@ -152,7 +153,7 @@ namespace GodotTools
}
}
- public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines)
+ public static bool BuildProjectBlocking(string config, [CanBeNull] string platform = null)
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
@@ -168,29 +169,18 @@ namespace GodotTools
return false;
}
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
-
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
{
pr.Step("Building project solution", 0);
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Restore", "Build"}, config);
-
- bool escapeNeedsDoubleBackslash = buildTool == BuildTool.MsBuildMono || buildTool == BuildTool.DotnetCli;
-
- // Add Godot defines
- string constants = !escapeNeedsDoubleBackslash ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Build"}, config, restore: true);
- foreach (var godotDefine in godotDefines)
- constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};";
+ // 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}");
if (Internal.GodotIsRealTDouble())
- constants += "GODOT_REAL_T_IS_DOUBLE;";
-
- constants += !escapeNeedsDoubleBackslash ? "\"" : "\\\"";
-
- buildInfo.CustomProperties.Add(constants);
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
if (!Build(buildInfo))
{
@@ -215,31 +205,10 @@ namespace GodotTools
if (File.Exists(editorScriptsMetadataPath))
File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
- var currentPlayRequest = GodotSharpEditor.Instance.CurrentPlaySettings;
-
- if (currentPlayRequest != null)
- {
- if (currentPlayRequest.Value.HasDebugger)
- {
- // Set the environment variable that will tell the player to connect to the IDE debugger
- // TODO: We should probably add a better way to do this
- Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT",
- "--debugger-agent=transport=dt_socket" +
- $",address={currentPlayRequest.Value.DebuggerHost}:{currentPlayRequest.Value.DebuggerPort}" +
- ",server=n");
- }
-
- if (!currentPlayRequest.Value.BuildBeforePlaying)
- return true; // Requested play from an external editor/IDE which already built the project
- }
-
- var godotDefines = new[]
- {
- Godot.OS.GetName(),
- Internal.GodotIs32Bits() ? "32" : "64"
- };
+ if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
+ return true; // Requested play from an external editor/IDE which already built the project
- return BuildProjectBlocking("Debug", godotDefines);
+ return BuildProjectBlocking("Debug");
}
public static void Initialize()
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
index 0106e1f1ac..8596cd24af 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
@@ -113,7 +113,7 @@ namespace GodotTools
throw new IndexOutOfRangeException("Item list index out of range");
// Get correct issue idx from issue list
- int issueIndex = (int)issuesList.GetItemMetadata(idx);
+ int issueIndex = (int)(long)issuesList.GetItemMetadata(idx);
if (issueIndex < 0 || issueIndex >= issues.Count)
throw new IndexOutOfRangeException("Issue index out of range");
diff --git a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
index 421729cc11..1d800b8151 100644
--- a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
@@ -1,9 +1,9 @@
using Godot;
using System;
+using System.Linq;
using Godot.Collections;
using GodotTools.Internals;
using GodotTools.ProjectEditor;
-using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using Directory = GodotTools.Utils.Directory;
@@ -15,7 +15,7 @@ namespace GodotTools
{
try
{
- return ProjectGenerator.GenGameProject(dir, name, compileItems: new string[] { });
+ return ProjectGenerator.GenAndSaveGameProject(dir, name);
}
catch (Exception e)
{
@@ -24,14 +24,6 @@ namespace GodotTools
}
}
- public static void AddItem(string projectPath, string itemType, string include)
- {
- if (!(bool)GlobalDef("mono/project/auto_update_project", true))
- return;
-
- ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);
- }
-
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static ulong ConvertToTimestamp(this DateTime value)
@@ -40,81 +32,77 @@ namespace GodotTools
return (ulong)elapsedTime.TotalSeconds;
}
- public static void GenerateScriptsMetadata(string projectPath, string outputPath)
+ private static bool TryParseFileMetadata(string includeFile, ulong modifiedTime, out Dictionary fileMetadata)
{
- if (File.Exists(outputPath))
- File.Delete(outputPath);
+ fileMetadata = null;
- var oldDict = Internal.GetScriptsMetadataOrNothing();
- var newDict = new Godot.Collections.Dictionary<string, object>();
+ var parseError = ScriptClassParser.ParseFile(includeFile, out var classes, out string errorStr);
- foreach (var includeFile in ProjectUtils.GetIncludeFiles(projectPath, "Compile"))
+ if (parseError != Error.Ok)
{
- string projectIncludeFile = ("res://" + includeFile).SimplifyGodotPath();
+ GD.PushError($"Failed to determine namespace and class for script: {includeFile}. Parse error: {errorStr ?? parseError.ToString()}");
+ return false;
+ }
- ulong modifiedTime = File.GetLastWriteTime(projectIncludeFile).ConvertToTimestamp();
+ string searchName = System.IO.Path.GetFileNameWithoutExtension(includeFile);
- if (oldDict.TryGetValue(projectIncludeFile, out var oldFileVar))
- {
- var oldFileDict = (Dictionary)oldFileVar;
-
- if (ulong.TryParse(oldFileDict["modified_time"] as string, out ulong storedModifiedTime))
- {
- if (storedModifiedTime == modifiedTime)
- {
- // No changes so no need to parse again
- newDict[projectIncludeFile] = oldFileDict;
- continue;
- }
- }
- }
+ var firstMatch = classes.FirstOrDefault(classDecl =>
+ classDecl.BaseCount != 0 && // If it doesn't inherit anything, it can't be a Godot.Object.
+ classDecl.SearchName == searchName // Filter by the name we're looking for
+ );
+
+ if (firstMatch == null)
+ return false; // Not found
- Error parseError = ScriptClassParser.ParseFile(projectIncludeFile, out var classes, out string errorStr);
- if (parseError != Error.Ok)
+ fileMetadata = new Dictionary
+ {
+ ["modified_time"] = $"{modifiedTime}",
+ ["class"] = new Dictionary
{
- GD.PushError($"Failed to determine namespace and class for script: {projectIncludeFile}. Parse error: {errorStr ?? parseError.ToString()}");
- continue;
+ ["namespace"] = firstMatch.Namespace,
+ ["class_name"] = firstMatch.Name,
+ ["nested"] = firstMatch.Nested
}
+ };
- string searchName = System.IO.Path.GetFileNameWithoutExtension(projectIncludeFile);
-
- var classDict = new Dictionary();
+ return true;
+ }
- foreach (var classDecl in classes)
- {
- if (classDecl.BaseCount == 0)
- continue; // Does not inherit nor implement anything, so it can't be a script class
+ public static void GenerateScriptsMetadata(string projectPath, string outputPath)
+ {
+ var metadataDict = Internal.GetScriptsMetadataOrNothing().Duplicate();
- string classCmp = classDecl.Nested ?
- classDecl.Name.Substring(classDecl.Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
- classDecl.Name;
+ bool IsUpToDate(string includeFile, ulong modifiedTime)
+ {
+ return metadataDict.TryGetValue(includeFile, out var oldFileVar) &&
+ ulong.TryParse(((Dictionary)oldFileVar)["modified_time"] as string,
+ out ulong storedModifiedTime) && storedModifiedTime == modifiedTime;
+ }
- if (classCmp != searchName)
- continue;
+ var outdatedFiles = ProjectUtils.GetIncludeFiles(projectPath, "Compile")
+ .Select(path => ("res://" + path).SimplifyGodotPath())
+ .ToDictionary(path => path, path => File.GetLastWriteTime(path).ConvertToTimestamp())
+ .Where(pair => !IsUpToDate(includeFile: pair.Key, modifiedTime: pair.Value))
+ .ToArray();
- classDict["namespace"] = classDecl.Namespace;
- classDict["class_name"] = classDecl.Name;
- classDict["nested"] = classDecl.Nested;
- break;
- }
+ foreach (var pair in outdatedFiles)
+ {
+ metadataDict.Remove(pair.Key);
- if (classDict.Count == 0)
- continue; // Not found
+ string includeFile = pair.Key;
- newDict[projectIncludeFile] = new Dictionary { ["modified_time"] = $"{modifiedTime}", ["class"] = classDict };
+ if (TryParseFileMetadata(includeFile, modifiedTime: pair.Value, out var fileMetadata))
+ metadataDict[includeFile] = fileMetadata;
}
- if (newDict.Count > 0)
- {
- string json = JSON.Print(newDict);
+ string json = metadataDict.Count <= 0 ? "{}" : JSON.Print(metadataDict);
- string baseDir = outputPath.GetBaseDir();
+ string baseDir = outputPath.GetBaseDir();
- if (!Directory.Exists(baseDir))
- Directory.CreateDirectory(baseDir);
+ if (!Directory.Exists(baseDir))
+ Directory.CreateDirectory(baseDir);
- File.WriteAllText(outputPath, json);
- }
+ File.WriteAllText(outputPath, json);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index f60e469503..42ede3f3f3 100755
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -328,7 +328,7 @@ MONO_AOT_MODE_LAST = 1000,
if (lipoExitCode != 0)
throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}");
- // TODO: Add the AOT lib and interpreter libs as device only to supress warnings when targeting the simulator
+ // TODO: Add the AOT lib and interpreter libs as device only to suppress warnings when targeting the simulator
// Add the fat AOT static library to the Xcode project
exporter.AddIosProjectStaticLib(fatOutputFilePath);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 6bfbc62f3b..599ca94699 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using GodotTools.Core;
using GodotTools.Internals;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
@@ -18,7 +19,7 @@ namespace GodotTools.Export
public class ExportPlugin : EditorExportPlugin
{
[Flags]
- enum I18NCodesets
+ enum I18NCodesets : long
{
None = 0,
CJK = 1,
@@ -145,9 +146,7 @@ namespace GodotTools.Export
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return;
- string platform = DeterminePlatformFromFeatures(features);
-
- if (platform == null)
+ if (!DeterminePlatformFromFeatures(features, out string platform))
throw new NotSupportedException("Target platform not supported");
string outputDir = new FileInfo(path).Directory?.FullName ??
@@ -160,10 +159,7 @@ namespace GodotTools.Export
AddFile(scriptsMetadataPath, scriptsMetadataPath);
- // Turn export features into defines
- var godotDefines = features;
-
- if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines))
+ if (!BuildManager.BuildProjectBlocking(buildConfig, platform))
throw new Exception("Failed to build project");
// Add dependency assemblies
@@ -289,6 +285,7 @@ namespace GodotTools.Export
}
}
+ [NotNull]
private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
{
string target = isDebug ? "release_debug" : "release";
@@ -343,18 +340,19 @@ namespace GodotTools.Export
private static bool PlatformHasTemplateDir(string platform)
{
// OSX 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.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
+ return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform);
}
- private static string DeterminePlatformFromFeatures(IEnumerable<string> features)
+ private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
{
foreach (var feature in features)
{
- if (OS.PlatformNameMap.TryGetValue(feature, out string platform))
- return platform;
+ if (OS.PlatformNameMap.TryGetValue(feature, out platform))
+ return true;
}
- return null;
+ platform = null;
+ return false;
}
private static string GetBclProfileDir(string profile)
@@ -391,7 +389,7 @@ namespace GodotTools.Export
/// </summary>
private static bool PlatformRequiresCustomBcl(string platform)
{
- if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(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.
@@ -432,7 +430,7 @@ namespace GodotTools.Export
private static string DetermineDataDirNameForProject()
{
var appName = (string)ProjectSettings.GetSetting("application/config/name");
- string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
+ string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
index bb218c2f19..90d6eb960e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/ExternalEditorId.cs
@@ -1,6 +1,6 @@
namespace GodotTools
{
- public enum ExternalEditorId
+ public enum ExternalEditorId : long
{
None,
VisualStudio, // TODO (Windows-only)
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index eb7696685f..2a450c5b87 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Linq;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
@@ -29,7 +30,8 @@ namespace GodotTools
private AcceptDialog aboutDialog;
private CheckBox aboutDialogCheckBox;
- private ToolButton bottomPanelBtn;
+ private Button bottomPanelBtn;
+ private Button toolBarBuildButton;
public GodotIdeManager GodotIdeManager { get; private set; }
@@ -37,13 +39,14 @@ namespace GodotTools
public BottomPanel BottomPanel { get; private set; }
- public PlaySettings? CurrentPlaySettings { get; set; }
+ public bool SkipBuildBeforePlaying { get; set; } = false;
public static string ProjectAssemblyName
{
get
{
var projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ projectAssemblyName = projectAssemblyName.ToSafeDirName();
if (string.IsNullOrEmpty(projectAssemblyName))
projectAssemblyName = "UnnamedProject";
return projectAssemblyName;
@@ -125,6 +128,7 @@ namespace GodotTools
{
menuPopup.RemoveItem(menuPopup.GetItemIndex((int)MenuOptions.CreateSln));
bottomPanelBtn.Show();
+ toolBarBuildButton.Show();
}
private void _ShowAboutDialog()
@@ -174,36 +178,6 @@ namespace GodotTools
// Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
aboutDialog.Exclusive = false;
}
-
- var fileSystemDock = GetEditorInterface().GetFileSystemDock();
-
- fileSystemDock.FilesMoved += (file, newFile) =>
- {
- if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
- {
- ProjectUtils.RenameItemInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(file), ProjectSettings.GlobalizePath(newFile));
- }
- };
-
- fileSystemDock.FileRemoved += file =>
- {
- if (Path.GetExtension(file) == Internal.CSharpLanguageExtension)
- ProjectUtils.RemoveItemFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(file));
- };
-
- fileSystemDock.FolderMoved += (oldFolder, newFolder) =>
- {
- ProjectUtils.RenameItemsToNewFolderInProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(oldFolder), ProjectSettings.GlobalizePath(newFolder));
- };
-
- fileSystemDock.FolderRemoved += oldFolder =>
- {
- ProjectUtils.RemoveItemsInFolderFromProjectChecked(GodotSharpDirs.ProjectCsProjPath, "Compile",
- ProjectSettings.GlobalizePath(oldFolder));
- };
}
}
@@ -238,7 +212,31 @@ namespace GodotTools
// Not an error. Tells the caller to fallback to the global external editor settings or the built-in editor.
return Error.Unavailable;
case ExternalEditorId.VisualStudio:
- throw new NotSupportedException();
+ {
+ string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
+
+ var args = new List<string>
+ {
+ GodotSharpDirs.ProjectSlnPath,
+ line >= 0 ? $"{scriptPath};{line + 1};{col + 1}" : scriptPath
+ };
+
+ string command = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "GodotTools.OpenVisualStudio.exe");
+
+ try
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ 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}'");
+ }
+
+ break;
+ }
case ExternalEditorId.VisualStudioForMac:
goto case ExternalEditorId.MonoDevelop;
case ExternalEditorId.Rider:
@@ -364,6 +362,37 @@ namespace GodotTools
return BuildManager.EditorBuildCallback();
}
+ private void ApplyNecessaryChangesToSolution()
+ {
+ try
+ {
+ // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
+ DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
+
+ var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
+ ?? throw new Exception("Cannot open C# project");
+
+ // 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.EnsureGodotSdkIsUpToDate(msbuildProject);
+
+ if (msbuildProject.HasUnsavedChanges)
+ {
+ // Save a copy of the project before replacing it
+ FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
+
+ msbuildProject.Save();
+ }
+ }
+ catch (Exception e)
+ {
+ GD.PushError(e.ToString());
+ }
+ }
+
public override void EnablePlugin()
{
base.EnablePlugin();
@@ -421,7 +450,7 @@ namespace GodotTools
aboutLabel.Text =
"C# support in Godot Engine is in late alpha stage and, while already usable, " +
"it is not meant for use in production.\n\n" +
- "Projects can be exported to Linux, macOS, Windows and Android, but not yet to iOS, HTML5 or UWP. " +
+ "Projects can be exported to Linux, macOS, Windows, Android, iOS and HTML5, but not yet to UWP. " +
"Bugs and usability issues will be addressed gradually over future releases, " +
"potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
"If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +
@@ -441,59 +470,28 @@ namespace GodotTools
aboutVBox.AddChild(aboutDialogCheckBox);
}
- if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
+ toolBarBuildButton = new Button
{
- try
- {
- // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
- DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
-
- var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
- ?? throw new Exception("Cannot open C# project");
-
- // NOTE: The order in which changes are made to the project is important
-
- // Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
- ProjectUtils.MigrateFromOldConfigNames(msbuildProject);
-
- // Apply the other fixes only after configurations have been migrated
-
- // Make sure the existing project has Api assembly references configured correctly
- ProjectUtils.FixApiHintPath(msbuildProject);
-
- // Make sure the existing project references the Microsoft.NETFramework.ReferenceAssemblies nuget package
- ProjectUtils.EnsureHasNugetNetFrameworkRefAssemblies(msbuildProject);
-
- if (msbuildProject.HasUnsavedChanges)
- {
- // Save a copy of the project before replacing it
- FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
+ Text = "Build",
+ HintTooltip = "Build solution",
+ FocusMode = Control.FocusModeEnum.None
+ };
+ toolBarBuildButton.PressedSignal += _BuildSolutionPressed;
+ AddControlToContainer(CustomControlContainer.Toolbar, toolBarBuildButton);
- msbuildProject.Save();
- }
- }
- catch (Exception e)
- {
- GD.PushError(e.ToString());
- }
+ if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
+ {
+ ApplyNecessaryChangesToSolution();
}
else
{
bottomPanelBtn.Hide();
+ toolBarBuildButton.Hide();
menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln);
}
menuPopup.IdPressed += _MenuOptionPressed;
- var buildButton = new ToolButton
- {
- Text = "Build",
- HintTooltip = "Build solution",
- FocusMode = Control.FocusModeEnum.None
- };
- buildButton.PressedSignal += _BuildSolutionPressed;
- AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
-
// External editor settings
EditorDef("mono/editor/external_editor", ExternalEditorId.None);
@@ -501,7 +499,8 @@ namespace GodotTools
if (OS.IsWindows)
{
- settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
+ settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" +
+ $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index ba527ca3b5..3f14629b11 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -33,5 +33,7 @@
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
+ <!-- Include it if this is an SCons build targeting Windows, or if it's not an SCons build but we're on Windows -->
+ <ProjectReference Include="..\GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) " />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index ae05710f4f..b30c857c64 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -10,7 +10,7 @@ namespace GodotTools
public override void _Notification(int what)
{
- if (what == Node.NotificationWmFocusIn)
+ if (what == Node.NotificationWmWindowFocusIn)
{
RestartTimer();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 32f264d100..eb34a2d0f7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -12,6 +12,7 @@ using GodotTools.IdeMessaging;
using GodotTools.IdeMessaging.Requests;
using GodotTools.IdeMessaging.Utils;
using GodotTools.Internals;
+using GodotTools.Utils;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
using File = System.IO.File;
@@ -307,6 +308,11 @@ namespace GodotTools.Ides
var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);
return await HandleDebugPlay(request);
},
+ [StopPlayRequest.Id] = async (peer, content) =>
+ {
+ var request = JsonConvert.DeserializeObject<StopPlayRequest>(content.Body);
+ return await HandleStopPlay(request);
+ },
[ReloadScriptsRequest.Id] = async (peer, content) =>
{
_ = JsonConvert.DeserializeObject<ReloadScriptsRequest>(content.Body);
@@ -324,9 +330,10 @@ namespace GodotTools.Ides
{
DispatchToMainThread(() =>
{
- GodotSharpEditor.Instance.CurrentPlaySettings = new PlaySettings();
+ // TODO: Add BuildBeforePlaying flag to PlayRequest
+
+ // Run the game
Internal.EditorRunPlay();
- GodotSharpEditor.Instance.CurrentPlaySettings = null;
});
return Task.FromResult<Response>(new PlayResponse());
}
@@ -335,14 +342,32 @@ namespace GodotTools.Ides
{
DispatchToMainThread(() =>
{
- GodotSharpEditor.Instance.CurrentPlaySettings =
- new PlaySettings(request.DebuggerHost, request.DebuggerPort, request.BuildBeforePlaying ?? true);
+ // Tell the build callback whether the editor already built the solution or not
+ GodotSharpEditor.Instance.SkipBuildBeforePlaying = !(request.BuildBeforePlaying ?? true);
+
+ // Pass the debugger agent settings to the player via an environment variables
+ // TODO: It would be better if this was an argument in EditorRunPlay instead
+ Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT",
+ "--debugger-agent=transport=dt_socket" +
+ $",address={request.DebuggerHost}:{request.DebuggerPort}" +
+ ",server=n");
+
+ // Run the game
Internal.EditorRunPlay();
- GodotSharpEditor.Instance.CurrentPlaySettings = null;
+
+ // Restore normal settings
+ Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", "");
+ GodotSharpEditor.Instance.SkipBuildBeforePlaying = false;
});
return Task.FromResult<Response>(new DebugPlayResponse());
}
+ private static Task<Response> HandleStopPlay(StopPlayRequest request)
+ {
+ DispatchToMainThread(Internal.EditorRunStop);
+ return Task.FromResult<Response>(new StopPlayResponse());
+ }
+
private static Task<Response> HandleReloadScripts()
{
DispatchToMainThread(Internal.ScriptEditorDebugger_ReloadScripts);
@@ -351,8 +376,13 @@ namespace GodotTools.Ides
private static async Task<Response> HandleCodeCompletionRequest(CodeCompletionRequest request)
{
+ // This is needed if the "resource path" part of the path is case insensitive.
+ // However, it doesn't fix resource loading if the rest of the path is also case insensitive.
+ string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
+
var response = new CodeCompletionResponse {Kind = request.Kind, ScriptFile = request.ScriptFile};
- response.Suggestions = await Task.Run(() => Internal.CodeCompletionRequest(response.Kind, response.ScriptFile));
+ response.Suggestions = await Task.Run(() =>
+ Internal.CodeCompletionRequest(response.Kind, scriptFileLocalized ?? request.ScriptFile));
return response;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
index 7fb087467f..c72a84c513 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
@@ -13,9 +13,13 @@ namespace GodotTools.Internals
public string Name { get; }
public string Namespace { get; }
public bool Nested { get; }
- public int BaseCount { get; }
+ public long BaseCount { get; }
- public ClassDecl(string name, string @namespace, bool nested, int baseCount)
+ public string SearchName => Nested ?
+ Name.Substring(Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
+ Name;
+
+ public ClassDecl(string name, string @namespace, bool nested, long baseCount)
{
Name = name;
Namespace = @namespace;
@@ -45,7 +49,7 @@ namespace GodotTools.Internals
(string)classDeclDict["name"],
(string)classDeclDict["namespace"],
(bool)classDeclDict["nested"],
- (int)classDeclDict["base_count"]
+ (long)classDeclDict["base_count"]
));
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
new file mode 100644
index 0000000000..c6724ccaf7
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
@@ -0,0 +1,48 @@
+using System;
+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 bool PathStartsWithAlreadyNorm(this string childPath, string parentPath)
+ {
+ // This won't work for Linux/macOS case insensitive file systems, but it's enough for our current problems
+ bool caseSensitive = !OS.IsWindows;
+
+ string parentPathNorm = parentPath.NormalizePath() + Path.DirectorySeparatorChar;
+ string childPathNorm = childPath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ return childPathNorm.StartsWith(parentPathNorm,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool PathStartsWith(this string childPath, string parentPath)
+ {
+ string childPathNorm = childPath.NormalizePath() + Path.DirectorySeparatorChar;
+ string parentPathNorm = parentPath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
+ }
+
+ [CanBeNull]
+ public static string LocalizePathWithCaseChecked(string path)
+ {
+ string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
+ string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
+
+ if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm))
+ return null;
+
+ string result = "res://" + pathNorm.Substring(resourcePathNorm.Length);
+
+ // Remove the last separator we added
+ return result.Substring(0, result.Length - 1);
+ }
+ }
+}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 0218773105..a17c371117 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -45,7 +45,6 @@
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
-#include "csharp_project.h"
#define CS_INDENT " " // 4 whitespaces
@@ -62,10 +61,8 @@
#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3
#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4
-#define OPEN_BLOCK_L4 INDENT4 OPEN_BLOCK INDENT5
#define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
#define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
-#define CLOSE_BLOCK_L4 INDENT4 CLOSE_BLOCK
#define CS_FIELD_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
@@ -123,8 +120,9 @@ static String snake_to_pascal_case(const String &p_identifier, bool p_input_is_u
if (part.length()) {
part[0] = _find_upper(part[0]);
if (p_input_is_upper) {
- for (int j = 1; j < part.length(); j++)
+ for (int j = 1; j < part.length(); j++) {
part[j] = _find_lower(part[j]);
+ }
}
ret += part;
} else {
@@ -157,8 +155,9 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up
part[0] = _find_upper(part[0]);
}
if (p_input_is_upper) {
- for (int j = i != 0 ? 1 : 0; j < part.length(); j++)
+ for (int j = i != 0 ? 1 : 0; j < part.length(); j++) {
part[j] = _find_lower(part[j]);
+ }
}
ret += part;
} else {
@@ -182,8 +181,9 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up
String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) {
// Based on the version in EditorHelp
- if (p_bbcode.empty())
+ if (p_bbcode.empty()) {
return String();
+ }
DocData *doc = EditorHelp::get_doc_data();
@@ -200,8 +200,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
while (pos < bbcode.length()) {
int brk_pos = bbcode.find("[", pos);
- if (brk_pos < 0)
+ if (brk_pos < 0) {
brk_pos = bbcode.length();
+ }
if (brk_pos > pos) {
String text = bbcode.substr(pos, brk_pos - pos);
@@ -210,19 +211,22 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
} else {
Vector<String> lines = text.split("\n");
for (int i = 0; i < lines.size(); i++) {
- if (i != 0)
+ if (i != 0) {
xml_output.append("<para>");
+ }
xml_output.append(lines[i].xml_escape());
- if (i != lines.size() - 1)
+ if (i != lines.size() - 1) {
xml_output.append("</para>\n");
+ }
}
}
}
- if (brk_pos == bbcode.length())
+ if (brk_pos == bbcode.length()) {
break; // nothing else to add
+ }
int brk_end = bbcode.find("]", brk_pos + 1);
@@ -233,13 +237,15 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
} else {
Vector<String> lines = text.split("\n");
for (int i = 0; i < lines.size(); i++) {
- if (i != 0)
+ if (i != 0) {
xml_output.append("<para>");
+ }
xml_output.append(lines[i].xml_escape());
- if (i != lines.size() - 1)
+ if (i != lines.size() - 1) {
xml_output.append("</para>\n");
+ }
}
}
@@ -412,8 +418,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
for (const List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
target_ienum = &E->get();
target_iconst = find_constant_by_name(target_name, target_ienum->constants);
- if (target_iconst)
+ if (target_iconst) {
break;
+ }
}
if (target_iconst) {
@@ -450,8 +457,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
for (const List<EnumInterface>::Element *E = target_itype->enums.front(); E; E = E->next()) {
target_ienum = &E->get();
target_iconst = find_constant_by_name(target_name, target_ienum->constants);
- if (target_iconst)
+ if (target_iconst) {
break;
+ }
}
if (target_iconst) {
@@ -583,8 +591,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
tag_stack.push_front(tag);
} else if (tag == "url") {
int end = bbcode.find("[", brk_end);
- if (end == -1)
+ if (end == -1) {
end = bbcode.length();
+ }
String url = bbcode.substr(brk_end + 1, end - brk_end - 1);
xml_output.append("<a href=\"");
xml_output.append(url);
@@ -603,8 +612,9 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
tag_stack.push_front("url");
} else if (tag == "img") {
int end = bbcode.find("[", brk_end);
- if (end == -1)
+ if (end == -1) {
end = bbcode.length();
+ }
String image = bbcode.substr(brk_end + 1, end - brk_end - 1);
// Not supported. Just append the bbcode.
@@ -640,8 +650,9 @@ int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
Vector<String> front_parts = front_iconstant.name.split("_", /* p_allow_empty: */ true);
int candidate_len = front_parts.size() - 1;
- if (candidate_len == 0)
+ if (candidate_len == 0) {
return 0;
+ }
for (const List<ConstantInterface>::Element *E = p_ienum.constants.front()->next(); E; E = E->next()) {
const ConstantInterface &iconstant = E->get();
@@ -653,14 +664,16 @@ int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
if (front_parts[i] != parts[i]) {
// HARDCODED: Some Flag enums have the prefix 'FLAG_' for everything except 'FLAGS_DEFAULT' (same for 'METHOD_FLAG_' and'METHOD_FLAGS_DEFAULT').
bool hardcoded_exc = (i == candidate_len - 1 && ((front_parts[i] == "FLAGS" && parts[i] == "FLAG") || (front_parts[i] == "FLAG" && parts[i] == "FLAGS")));
- if (!hardcoded_exc)
+ if (!hardcoded_exc) {
break;
+ }
}
}
candidate_len = i;
- if (candidate_len == 0)
+ if (candidate_len == 0) {
return 0;
+ }
}
return candidate_len;
@@ -677,22 +690,25 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
Vector<String> parts = constant_name.split("_", /* p_allow_empty: */ true);
- if (parts.size() <= curr_prefix_length)
+ if (parts.size() <= curr_prefix_length) {
continue;
+ }
if (parts[curr_prefix_length][0] >= '0' && parts[curr_prefix_length][0] <= '9') {
// The name of enum constants may begin with a numeric digit when strip from the enum prefix,
// so we make the prefix for this constant one word shorter in those cases.
for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) {
- if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9')
+ if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9') {
break;
+ }
}
}
constant_name = "";
for (int i = curr_prefix_length; i < parts.size(); i++) {
- if (i > curr_prefix_length)
+ if (i > curr_prefix_length) {
constant_name += "_";
+ }
constant_name += parts[i];
}
@@ -705,8 +721,9 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
for (const List<MethodInterface>::Element *E = p_itype.methods.front(); E; E = E->next()) {
const MethodInterface &imethod = E->get();
- if (imethod.is_virtual)
+ if (imethod.is_virtual) {
continue;
+ }
const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type);
@@ -755,8 +772,9 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
List<InternalCall>::Element *match = method_icalls.find(im_icall);
if (match) {
- if (p_itype.api_type != ClassDB::API_EDITOR)
+ if (p_itype.api_type != ClassDB::API_EDITOR) {
match->get().editor_only = false;
+ }
method_icalls_map.insert(&E->get(), &match->get());
} else {
List<InternalCall>::Element *added = method_icalls.push_back(im_icall);
@@ -801,8 +819,9 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(";");
}
- if (!global_constants.empty())
+ if (!global_constants.empty()) {
p_output.append("\n");
+ }
p_output.append(INDENT1 CLOSE_BLOCK); // end of GD class
@@ -864,8 +883,9 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(INDENT1 CLOSE_BLOCK);
- if (enum_in_static_class)
+ if (enum_in_static_class) {
p_output.append(INDENT1 CLOSE_BLOCK);
+ }
}
p_output.append(CLOSE_BLOCK); // end of namespace
@@ -899,8 +919,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
_generate_global_constants(constants_source);
String output_file = path::join(base_gen_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_constants.cs");
Error save_err = _save_file(output_file, constants_source);
- if (save_err != OK)
+ if (save_err != OK) {
return save_err;
+ }
compile_items.push_back(output_file);
}
@@ -908,17 +929,20 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
const TypeInterface &itype = E.get();
- if (itype.api_type == ClassDB::API_EDITOR)
+ if (itype.api_type == ClassDB::API_EDITOR) {
continue;
+ }
String output_file = path::join(godot_objects_gen_dir, itype.proxy_name + ".cs");
Error err = _generate_cs_type(itype, output_file);
- if (err == ERR_SKIP)
+ if (err == ERR_SKIP) {
continue;
+ }
- if (err != OK)
+ if (err != OK) {
return err;
+ }
compile_items.push_back(output_file);
}
@@ -949,10 +973,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
- for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next())
+ for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) {
ADD_INTERNAL_CALL(E->get());
- for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next())
+ }
+ for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
ADD_INTERNAL_CALL(E->get());
+ }
#undef ADD_INTERNAL_CALL
@@ -961,8 +987,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
- if (err != OK)
+ if (err != OK) {
return err;
+ }
compile_items.push_back(internal_methods_file);
@@ -981,8 +1008,9 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
String includes_props_file = path::join(base_gen_dir, "GeneratedIncludes.props");
err = _save_file(includes_props_file, includes_props_content);
- if (err != OK)
+ if (err != OK) {
return err;
+ }
return OK;
}
@@ -1010,17 +1038,20 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
const TypeInterface &itype = E.get();
- if (itype.api_type != ClassDB::API_EDITOR)
+ if (itype.api_type != ClassDB::API_EDITOR) {
continue;
+ }
String output_file = path::join(godot_objects_gen_dir, itype.proxy_name + ".cs");
Error err = _generate_cs_type(itype, output_file);
- if (err == ERR_SKIP)
+ if (err == ERR_SKIP) {
continue;
+ }
- if (err != OK)
+ if (err != OK) {
return err;
+ }
compile_items.push_back(output_file);
}
@@ -1050,10 +1081,12 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
- for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
+ for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) {
ADD_INTERNAL_CALL(E->get());
- for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next())
+ }
+ for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
ADD_INTERNAL_CALL(E->get());
+ }
#undef ADD_INTERNAL_CALL
@@ -1062,8 +1095,9 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
Error err = _save_file(internal_methods_file, cs_icalls_content);
- if (err != OK)
+ if (err != OK) {
return err;
+ }
compile_items.push_back(internal_methods_file);
@@ -1082,8 +1116,9 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
String includes_props_file = path::join(base_gen_dir, "GeneratedIncludes.props");
err = _save_file(includes_props_file, includes_props_content);
- if (err != OK)
+ if (err != OK) {
return err;
+ }
return OK;
}
@@ -1210,92 +1245,89 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(INDENT1 "{");
- if (class_doc) {
- // Add constants
-
- for (const List<ConstantInterface>::Element *E = itype.constants.front(); E; E = E->next()) {
- const ConstantInterface &iconstant = E->get();
+ // Add constants
- if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
- Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
+ for (const List<ConstantInterface>::Element *E = itype.constants.front(); E; E = E->next()) {
+ const ConstantInterface &iconstant = E->get();
- if (summary_lines.size()) {
- output.append(MEMBER_BEGIN "/// <summary>\n");
+ if (iconstant.const_doc && iconstant.const_doc->description.size()) {
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT2 "/// ");
- output.append(summary_lines[i]);
- output.append("\n");
- }
+ if (summary_lines.size()) {
+ output.append(MEMBER_BEGIN "/// <summary>\n");
- output.append(INDENT2 "/// </summary>");
+ for (int i = 0; i < summary_lines.size(); i++) {
+ output.append(INDENT2 "/// ");
+ output.append(summary_lines[i]);
+ output.append("\n");
}
- }
- output.append(MEMBER_BEGIN "public const int ");
- output.append(iconstant.proxy_name);
- output.append(" = ");
- output.append(itos(iconstant.value));
- output.append(";");
+ output.append(INDENT2 "/// </summary>");
+ }
}
- if (itype.constants.size())
- output.append("\n");
+ output.append(MEMBER_BEGIN "public const int ");
+ output.append(iconstant.proxy_name);
+ output.append(" = ");
+ output.append(itos(iconstant.value));
+ output.append(";");
+ }
- // Add enums
+ if (itype.constants.size()) {
+ output.append("\n");
+ }
- for (const List<EnumInterface>::Element *E = itype.enums.front(); E; E = E->next()) {
- const EnumInterface &ienum = E->get();
+ // Add enums
- ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG);
+ for (const List<EnumInterface>::Element *E = itype.enums.front(); E; E = E->next()) {
+ const EnumInterface &ienum = E->get();
- output.append(MEMBER_BEGIN "public enum ");
- output.append(ienum.cname.operator String());
- output.append(MEMBER_BEGIN OPEN_BLOCK);
+ ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG);
- for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
- const ConstantInterface &iconstant = F->get();
+ output.append(MEMBER_BEGIN "public enum ");
+ output.append(ienum.cname.operator String());
+ output.append(MEMBER_BEGIN OPEN_BLOCK);
- if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
- Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
+ for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
+ const ConstantInterface &iconstant = F->get();
- if (summary_lines.size()) {
- output.append(INDENT3 "/// <summary>\n");
+ if (iconstant.const_doc && iconstant.const_doc->description.size()) {
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
- for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT3 "/// ");
- output.append(summary_lines[i]);
- output.append("\n");
- }
+ if (summary_lines.size()) {
+ output.append(INDENT3 "/// <summary>\n");
- output.append(INDENT3 "/// </summary>\n");
+ for (int i = 0; i < summary_lines.size(); i++) {
+ output.append(INDENT3 "/// ");
+ output.append(summary_lines[i]);
+ output.append("\n");
}
- }
- output.append(INDENT3);
- output.append(iconstant.proxy_name);
- output.append(" = ");
- output.append(itos(iconstant.value));
- output.append(F != ienum.constants.back() ? ",\n" : "\n");
+ output.append(INDENT3 "/// </summary>\n");
+ }
}
- output.append(INDENT2 CLOSE_BLOCK);
+ output.append(INDENT3);
+ output.append(iconstant.proxy_name);
+ output.append(" = ");
+ output.append(itos(iconstant.value));
+ output.append(F != ienum.constants.back() ? ",\n" : "\n");
}
- // Add properties
-
- for (const List<PropertyInterface>::Element *E = itype.properties.front(); E; E = E->next()) {
- const PropertyInterface &iprop = E->get();
- Error prop_err = _generate_cs_property(itype, iprop, output);
- ERR_FAIL_COND_V_MSG(prop_err != OK, prop_err,
- "Failed to generate property '" + iprop.cname.operator String() +
- "' for class '" + itype.name + "'.");
- }
+ output.append(INDENT2 CLOSE_BLOCK);
}
- // TODO: BINDINGS_NATIVE_NAME_FIELD should be StringName, once we support it in C#
+ // Add properties
+
+ for (const List<PropertyInterface>::Element *E = itype.properties.front(); E; E = E->next()) {
+ const PropertyInterface &iprop = E->get();
+ Error prop_err = _generate_cs_property(itype, iprop, output);
+ ERR_FAIL_COND_V_MSG(prop_err != OK, prop_err,
+ "Failed to generate property '" + iprop.cname.operator String() +
+ "' for class '" + itype.name + "'.");
+ }
if (itype.is_singleton) {
// Add the type name and the singleton pointer as static fields
@@ -1368,15 +1400,17 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
if (itype.is_singleton) {
InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
- if (!find_icall_by_name(singleton_icall.name, custom_icalls))
+ if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
custom_icalls.push_back(singleton_icall);
+ }
}
if (is_derived_type && itype.is_instantiable) {
InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
- if (!find_icall_by_name(ctor_icall.name, custom_icalls))
+ if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
custom_icalls.push_back(ctor_icall);
+ }
}
output.append(INDENT1 CLOSE_BLOCK /* class */
@@ -1442,6 +1476,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
const TypeInterface *prop_itype = _get_type_or_null(proptype_name);
ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found
+ ERR_FAIL_COND_V_MSG(prop_itype->is_singleton, ERR_BUG,
+ "Property type is a singleton: '" + p_itype.name + "." + String(p_iprop.cname) + "'.");
+
if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -1461,8 +1498,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(MEMBER_BEGIN "public ");
- if (p_itype.is_singleton)
+ if (p_itype.is_singleton) {
p_output.append("static ");
+ }
p_output.append(prop_itype->cs_type);
p_output.append(" ");
@@ -1534,6 +1572,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
+ ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG,
+ "Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'.");
+
String method_bind_field = "__method_bind_" + itos(p_method_bind_count);
String arguments_sig;
@@ -1549,29 +1590,41 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
const ArgumentInterface &iarg = F->get();
const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ 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 + "'.");
+
+ if (iarg.default_argument.size()) {
+ CRASH_COND_MSG(!_arg_default_value_is_assignable_to_type(iarg.def_param_value, *arg_type),
+ "Invalid default value for parameter '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
+ }
+
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
- if (F != p_imethod.arguments.front())
+ if (F != p_imethod.arguments.front()) {
arguments_sig += ", ";
+ }
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
arguments_sig += "Nullable<";
+ }
arguments_sig += arg_type->cs_type;
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
arguments_sig += "> ";
- else
+ } else {
arguments_sig += " ";
+ }
arguments_sig += iarg.name;
if (iarg.default_argument.size()) {
- if (iarg.def_param_mode != ArgumentInterface::CONSTANT)
+ if (iarg.def_param_mode != ArgumentInterface::CONSTANT) {
arguments_sig += " = null";
- else
+ } else {
arguments_sig += " = " + sformat(iarg.default_argument, arg_type->cs_type);
+ }
}
}
@@ -1589,17 +1642,19 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_statements += " = ";
cs_in_statements += iarg.name;
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
cs_in_statements += ".HasValue ? ";
- else
+ } else {
cs_in_statements += " != null ? ";
+ }
cs_in_statements += iarg.name;
- if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL)
+ if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
cs_in_statements += ".Value : ";
- else
+ } else {
cs_in_statements += " : ";
+ }
String def_arg = sformat(iarg.default_argument, arg_type->cs_type);
@@ -1659,8 +1714,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
}
if (p_imethod.is_deprecated) {
- if (p_imethod.deprecation_message.empty())
+ if (p_imethod.deprecation_message.empty()) {
WARN_PRINT("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'.");
+ }
p_output.append(MEMBER_BEGIN "[Obsolete(\"");
p_output.append(p_imethod.deprecation_message);
@@ -1720,8 +1776,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
im_call += ".";
im_call += im_icall->name;
- if (p_imethod.arguments.size())
+ if (p_imethod.arguments.size()) {
p_output.append(cs_in_statements);
+ }
if (return_type->cname == name_cache.type_void) {
p_output.append(im_call + "(" + icall_params + ");\n");
@@ -1748,10 +1805,14 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
const ArgumentInterface &iarg = F->get();
const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ 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 + "'.");
+
// Add the current arguments to the signature
- if (F != p_isignal.arguments.front())
+ if (F != p_isignal.arguments.front()) {
arguments_sig += ", ";
+ }
arguments_sig += arg_type->cs_type;
arguments_sig += " ";
@@ -1778,8 +1839,9 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
if (p_isignal.is_deprecated) {
- if (p_isignal.deprecation_message.empty())
+ if (p_isignal.deprecation_message.empty()) {
WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
+ }
p_output.append(MEMBER_BEGIN "[Obsolete(\"");
p_output.append(p_isignal.deprecation_message);
@@ -1810,8 +1872,9 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Generate event
p_output.append(MEMBER_BEGIN "[Signal]" MEMBER_BEGIN "public ");
- if (p_itype.is_singleton)
+ if (p_itype.is_singleton) {
p_output.append("static ");
+ }
p_output.append("event ");
p_output.append(delegate_name);
@@ -1819,18 +1882,20 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append(p_isignal.proxy_name);
p_output.append("\n" OPEN_BLOCK_L2);
- if (p_itype.is_singleton)
+ if (p_itype.is_singleton) {
p_output.append("add => Singleton.Connect(__signal_name_");
- else
+ } else {
p_output.append("add => Connect(__signal_name_");
+ }
p_output.append(p_isignal.name);
p_output.append(", new Callable(value));\n");
- if (p_itype.is_singleton)
+ if (p_itype.is_singleton) {
p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_");
- else
+ } else {
p_output.append(INDENT3 "remove => Disconnect(__signal_name_");
+ }
p_output.append(p_isignal.name);
p_output.append(", new Callable(value));\n");
@@ -1864,7 +1929,6 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
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_reference);
CRASH_COND(itype.is_singleton);
}
@@ -1885,8 +1949,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
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))
+ if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
custom_icalls.push_back(singleton_icall);
+ }
output.append("Object* ");
output.append(singleton_icall_name);
@@ -1898,8 +1963,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
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))
+ if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
custom_icalls.push_back(ctor_icall);
+ }
output.append("Object* ");
output.append(ctor_method);
@@ -1998,8 +2064,9 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
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)
+ if (save_err != OK) {
return save_err;
+ }
OS::get_singleton()->print("Mono glue generated successfully\n");
@@ -2022,8 +2089,9 @@ Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p
}
Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) {
- if (p_imethod.is_virtual)
+ if (p_imethod.is_virtual) {
return OK; // Ignore
+ }
bool ret_void = p_imethod.return_type.cname == name_cache.type_void;
@@ -2051,10 +2119,12 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
c_in_statements += sformat(", &%s_in);\n", c_param_name);
}
} else {
- if (i > 0)
+ if (i > 0) {
c_args_var_content += ", ";
- if (arg_type->c_in.size())
+ }
+ if (arg_type->c_in.size()) {
c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
+ }
c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
}
@@ -2084,8 +2154,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
if (!generated_icall_funcs.find(im_icall)) {
generated_icall_funcs.push_back(im_icall);
- if (im_icall->editor_only)
+ if (im_icall->editor_only) {
p_output.append("#ifdef TOOLS_ENABLED\n");
+ }
// Generate icall function
@@ -2203,8 +2274,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
p_output.append(CLOSE_BLOCK "\n");
- if (im_icall->editor_only)
+ if (im_icall->editor_only) {
p_output.append("#endif // TOOLS_ENABLED\n");
+ }
}
return OK;
@@ -2213,19 +2285,22 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) {
const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname);
- if (builtin_type_match)
+ if (builtin_type_match) {
return &builtin_type_match->get();
+ }
const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname);
- if (obj_type_match)
+ if (obj_type_match) {
return &obj_type_match.get();
+ }
if (p_typeref.is_enum) {
const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname);
- if (enum_match)
+ if (enum_match) {
return &enum_match->get();
+ }
// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int);
@@ -2239,15 +2314,17 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
const TypeInterface *found = _get_type_or_null(p_typeref);
- if (found)
+ if (found) {
return found;
+ }
ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'.");
const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname);
- if (match)
+ if (match) {
return &match->get();
+ }
TypeInterface placeholder;
TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
@@ -2305,6 +2382,84 @@ StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Meta
}
}
+bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &p_val, const TypeInterface &p_arg_type) {
+ if (p_arg_type.name == name_cache.type_Variant) {
+ // Variant can take anything
+ return true;
+ }
+
+ switch (p_val.get_type()) {
+ case Variant::NIL:
+ return p_arg_type.is_object_type ||
+ name_cache.is_nullable_type(p_arg_type.name);
+ case Variant::BOOL:
+ return p_arg_type.name == name_cache.type_bool;
+ case Variant::INT:
+ return p_arg_type.name == name_cache.type_sbyte ||
+ p_arg_type.name == name_cache.type_short ||
+ p_arg_type.name == name_cache.type_int ||
+ p_arg_type.name == name_cache.type_byte ||
+ p_arg_type.name == name_cache.type_ushort ||
+ p_arg_type.name == name_cache.type_uint ||
+ p_arg_type.name == name_cache.type_long ||
+ p_arg_type.name == name_cache.type_ulong ||
+ p_arg_type.name == name_cache.type_float ||
+ p_arg_type.name == name_cache.type_double ||
+ p_arg_type.is_enum;
+ case Variant::FLOAT:
+ return p_arg_type.name == name_cache.type_float ||
+ p_arg_type.name == name_cache.type_double;
+ case Variant::STRING:
+ case Variant::STRING_NAME:
+ return p_arg_type.name == name_cache.type_String ||
+ p_arg_type.name == name_cache.type_StringName ||
+ p_arg_type.name == name_cache.type_NodePath;
+ case Variant::NODE_PATH:
+ return p_arg_type.name == name_cache.type_NodePath;
+ case Variant::TRANSFORM:
+ case Variant::TRANSFORM2D:
+ case Variant::BASIS:
+ case Variant::QUAT:
+ case Variant::PLANE:
+ case Variant::AABB:
+ case Variant::COLOR:
+ case Variant::VECTOR2:
+ 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:
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ case Variant::CALLABLE:
+ case Variant::SIGNAL:
+ return p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ case Variant::OBJECT:
+ return p_arg_type.is_object_type;
+ case Variant::VECTOR2I:
+ return p_arg_type.name == name_cache.type_Vector2 ||
+ p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ case Variant::RECT2I:
+ return p_arg_type.name == name_cache.type_Rect2 ||
+ p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ case Variant::VECTOR3I:
+ return p_arg_type.name == name_cache.type_Vector3 ||
+ p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ default:
+ CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
+ break;
+ }
+
+ return false;
+}
+
bool BindingsGenerator::_populate_object_type_interfaces() {
obj_types.clear();
@@ -2367,8 +2522,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
const PropertyInfo &property = E->get();
- if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY)
+ if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY) {
continue;
+ }
if (property.name.find("/") >= 0) {
// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
@@ -2380,10 +2536,12 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname);
iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname);
- if (iprop.setter != StringName())
+ if (iprop.setter != StringName()) {
accessor_methods[iprop.setter] = iprop.cname;
- if (iprop.getter != StringName())
+ }
+ if (iprop.getter != StringName()) {
accessor_methods[iprop.getter] = iprop.cname;
+ }
bool valid = false;
iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid);
@@ -2427,20 +2585,23 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
int argc = method_info.arguments.size();
- if (method_info.name.empty())
+ if (method_info.name.empty()) {
continue;
+ }
String cname = method_info.name;
- if (blacklisted_methods.find(itype.cname) && blacklisted_methods[itype.cname].find(cname))
+ if (blacklisted_methods.find(itype.cname) && blacklisted_methods[itype.cname].find(cname)) {
continue;
+ }
MethodInterface imethod;
imethod.name = method_info.name;
imethod.cname = cname;
- if (method_info.flags & METHOD_FLAG_VIRTUAL)
+ if (method_info.flags & METHOD_FLAG_VIRTUAL) {
imethod.is_virtual = true;
+ }
PropertyInfo return_info = method_info.return_val;
@@ -2462,9 +2623,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
// We assume the return type is void.
imethod.return_type.cname = name_cache.type_void;
- // Actually, more methods like this may be added in the future,
- // which could actually will return something different.
- // Let's put this to notify us if that ever happens.
+ // Actually, more methods like this may be added in the future, which could return
+ // something different. Let's put this check to notify us if that ever happens.
if (itype.cname != name_cache.type_Object || imethod.name != "free") {
WARN_PRINT("Notification: New unexpected virtual non-overridable method found."
" We only expected Object.free, but found '" +
@@ -2475,13 +2635,12 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
imethod.return_type.is_enum = true;
} else if (return_info.class_name != StringName()) {
imethod.return_type.cname = return_info.class_name;
- if (!imethod.is_virtual && ClassDB::is_parent_class(return_info.class_name, name_cache.type_Reference) && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE) {
- /* clang-format off */
- ERR_PRINT("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 + "'.");
- /* clang-format on */
- ERR_FAIL_V(false);
- }
+
+ bool bad_reference_hint = !imethod.is_virtual && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE &&
+ ClassDB::is_parent_class(return_info.class_name, name_cache.type_Reference);
+ ERR_FAIL_COND_V_MSG(bad_reference_hint, false,
+ 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.hint == PROPERTY_HINT_RESOURCE_TYPE) {
imethod.return_type.cname = return_info.hint_string;
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@@ -2572,6 +2731,10 @@ 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
if (!imethod.is_virtual && imethod.name[0] == '_') {
for (const List<PropertyInterface>::Element *F = itype.properties.front(); F; F = F->next()) {
const PropertyInterface &iprop = F->get();
@@ -2751,7 +2914,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
}
bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, ArgumentInterface &r_iarg) {
- r_iarg.default_argument = p_val;
+ r_iarg.def_param_value = p_val;
+ r_iarg.default_argument = p_val.operator String();
switch (p_val.get_type()) {
case Variant::NIL:
@@ -2784,8 +2948,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
}
break;
case Variant::TRANSFORM:
- if (p_val.operator Transform() == Transform())
+ if (p_val.operator Transform() == Transform()) {
r_iarg.default_argument.clear();
+ }
r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
@@ -2851,8 +3016,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
break;
}
- if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
+ 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;
+ }
return true;
}
@@ -3357,8 +3523,9 @@ void BindingsGenerator::_initialize() {
core_custom_icalls.clear();
editor_custom_icalls.clear();
- for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next())
+ for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
_generate_method_icalls(E.get());
+ }
initialized = true;
}
@@ -3426,21 +3593,25 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
}
if (glue_dir_path.length()) {
- if (bindings_generator.generate_glue(glue_dir_path) != OK)
+ if (bindings_generator.generate_glue(glue_dir_path) != OK) {
ERR_PRINT(generate_all_glue_option + ": Failed to generate the C++ glue.");
+ }
- if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK)
+ 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)
+ if (bindings_generator.generate_cs_api(cs_dir_path) != OK) {
ERR_PRINT(generate_cs_glue_option + ": Failed to generate the C# API.");
+ }
}
if (cpp_dir_path.length()) {
- if (bindings_generator.generate_glue(cpp_dir_path) != OK)
+ if (bindings_generator.generate_glue(cpp_dir_path) != OK) {
ERR_PRINT(generate_cpp_glue_option + ": Failed to generate the C++ glue.");
+ }
}
// Exit once done
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index aad109e03c..90c1c9f3ee 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -102,6 +102,8 @@ class BindingsGenerator {
TypeReference type;
String name;
+
+ Variant def_param_value;
DefaultParamMode def_param_mode = CONSTANT;
/**
@@ -355,8 +357,9 @@ class BindingsGenerator {
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().cname == p_cname)
+ if (E->get().cname == p_cname) {
return &E->get();
+ }
}
return nullptr;
@@ -364,8 +367,9 @@ class BindingsGenerator {
const PropertyInterface *find_property_by_name(const StringName &p_cname) const {
for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().cname == p_cname)
+ if (E->get().cname == p_cname) {
return &E->get();
+ }
}
return nullptr;
@@ -373,8 +377,9 @@ class BindingsGenerator {
const PropertyInterface *find_property_by_proxy_name(const String &p_proxy_name) const {
for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().proxy_name == p_proxy_name)
+ if (E->get().proxy_name == p_proxy_name) {
return &E->get();
+ }
}
return nullptr;
@@ -382,8 +387,9 @@ class BindingsGenerator {
const MethodInterface *find_method_by_proxy_name(const String &p_proxy_name) const {
for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().proxy_name == p_proxy_name)
+ if (E->get().proxy_name == p_proxy_name) {
return &E->get();
+ }
}
return nullptr;
@@ -523,58 +529,70 @@ class BindingsGenerator {
void _initialize_blacklisted_methods();
struct NameCache {
- StringName type_void;
- StringName type_Array;
- StringName type_Dictionary;
- StringName type_Variant;
- StringName type_VarArg;
- StringName type_Object;
- StringName type_Reference;
- StringName type_RID;
- StringName type_String;
- StringName type_StringName;
- StringName type_NodePath;
- StringName type_at_GlobalScope;
- StringName enum_Error;
-
- StringName type_sbyte;
- StringName type_short;
- StringName type_int;
- StringName type_long;
- StringName type_byte;
- StringName type_ushort;
- StringName type_uint;
- StringName type_ulong;
- StringName type_float;
- StringName type_double;
-
- NameCache() {
- type_void = StaticCString::create("void");
- type_Array = StaticCString::create("Array");
- type_Dictionary = StaticCString::create("Dictionary");
- type_Variant = StaticCString::create("Variant");
- type_VarArg = StaticCString::create("VarArg");
- type_Object = StaticCString::create("Object");
- type_Reference = StaticCString::create("Reference");
- type_RID = StaticCString::create("RID");
- type_String = StaticCString::create("String");
- type_StringName = StaticCString::create("StringName");
- type_NodePath = StaticCString::create("NodePath");
- type_at_GlobalScope = StaticCString::create("@GlobalScope");
- enum_Error = StaticCString::create("Error");
-
- type_sbyte = StaticCString::create("sbyte");
- type_short = StaticCString::create("short");
- type_int = StaticCString::create("int");
- type_long = StaticCString::create("long");
- type_byte = StaticCString::create("byte");
- type_ushort = StaticCString::create("ushort");
- type_uint = StaticCString::create("uint");
- type_ulong = StaticCString::create("ulong");
- type_float = StaticCString::create("float");
- type_double = StaticCString::create("double");
+ StringName type_void = StaticCString::create("void");
+ StringName type_Variant = StaticCString::create("Variant");
+ StringName type_VarArg = StaticCString::create("VarArg");
+ StringName type_Object = StaticCString::create("Object");
+ StringName type_Reference = StaticCString::create("Reference");
+ StringName type_RID = StaticCString::create("RID");
+ StringName type_String = StaticCString::create("String");
+ StringName type_StringName = StaticCString::create("StringName");
+ StringName type_NodePath = StaticCString::create("NodePath");
+ StringName type_at_GlobalScope = StaticCString::create("@GlobalScope");
+ StringName enum_Error = StaticCString::create("Error");
+
+ StringName type_sbyte = StaticCString::create("sbyte");
+ StringName type_short = StaticCString::create("short");
+ StringName type_int = StaticCString::create("int");
+ StringName type_byte = StaticCString::create("byte");
+ StringName type_ushort = StaticCString::create("ushort");
+ StringName type_uint = StaticCString::create("uint");
+ StringName type_long = StaticCString::create("long");
+ StringName type_ulong = StaticCString::create("ulong");
+
+ StringName type_bool = StaticCString::create("bool");
+ StringName type_float = StaticCString::create("float");
+ StringName type_double = StaticCString::create("double");
+
+ StringName type_Vector2 = StaticCString::create("Vector2");
+ StringName type_Rect2 = StaticCString::create("Rect2");
+ StringName type_Vector3 = StaticCString::create("Vector3");
+
+ // Object not included as it must be checked for all derived classes
+ static constexpr int nullable_types_count = 17;
+ StringName nullable_types[nullable_types_count] = {
+ type_String,
+ type_StringName,
+ type_NodePath,
+
+ StaticCString::create(_STR(Array)),
+ StaticCString::create(_STR(Dictionary)),
+ StaticCString::create(_STR(Callable)),
+ StaticCString::create(_STR(Signal)),
+
+ StaticCString::create(_STR(PackedByteArray)),
+ StaticCString::create(_STR(PackedInt32Array)),
+ StaticCString::create(_STR(PackedInt64rray)),
+ StaticCString::create(_STR(PackedFloat32Array)),
+ StaticCString::create(_STR(PackedFloat64Array)),
+ StaticCString::create(_STR(PackedStringArray)),
+ StaticCString::create(_STR(PackedVector2Array)),
+ StaticCString::create(_STR(PackedVector3Array)),
+ StaticCString::create(_STR(PackedColorArray)),
+ };
+
+ bool is_nullable_type(const StringName &p_type) const {
+ for (int i = 0; i < nullable_types_count; i++) {
+ if (p_type == nullable_types[i]) {
+ return true;
+ }
+ }
+
+ return false;
}
+ NameCache() {}
+
private:
NameCache(const NameCache &);
NameCache &operator=(const NameCache &);
@@ -585,8 +603,9 @@ class BindingsGenerator {
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)
+ if (it->get().name == p_name) {
return it;
+ }
it = it->next();
}
return nullptr;
@@ -594,20 +613,22 @@ class BindingsGenerator {
const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
for (const List<ConstantInterface>::Element *E = p_constants.front(); E; E = E->next()) {
- if (E->get().name == p_name)
+ if (E->get().name == p_name) {
return &E->get();
+ }
}
return nullptr;
}
inline String get_unique_sig(const TypeInterface &p_type) {
- if (p_type.is_reference)
+ if (p_type.is_reference) {
return "Ref";
- else if (p_type.is_object_type)
+ } else if (p_type.is_object_type) {
return "Obj";
- else if (p_type.is_enum)
+ } else if (p_type.is_enum) {
return "int";
+ }
return p_type.name;
}
@@ -626,6 +647,7 @@ class BindingsGenerator {
StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
bool _arg_default_value_from_variant(const Variant &p_val, ArgumentInterface &r_iarg);
+ bool _arg_default_value_is_assignable_to_type(const Variant &p_val, const TypeInterface &p_arg_type);
bool _populate_object_type_interfaces();
void _populate_builtin_type_interfaces();
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index 7a5e465e7a..942c6d26a6 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -45,8 +45,9 @@ _FORCE_INLINE_ String quoted(const String &p_str) {
}
void _add_nodes_suggestions(const Node *p_base, const Node *p_node, PackedStringArray &r_suggestions) {
- if (p_node != p_base && !p_node->get_owner())
+ if (p_node != p_base && !p_node->get_owner()) {
return;
+ }
String path_relative_to_orig = p_base->get_path_to(p_node);
@@ -58,18 +59,21 @@ void _add_nodes_suggestions(const Node *p_base, const Node *p_node, PackedString
}
Node *_find_node_for_script(Node *p_base, Node *p_current, const Ref<Script> &p_script) {
- if (p_current->get_owner() != p_base && p_base != p_current)
+ if (p_current->get_owner() != p_base && p_base != p_current) {
return nullptr;
+ }
Ref<Script> c = p_current->get_script();
- if (c == p_script)
+ if (c == p_script) {
return p_current;
+ }
for (int i = 0; i < p_current->get_child_count(); i++) {
Node *found = _find_node_for_script(p_base, p_current->get_child(i), p_script);
- if (found)
+ if (found) {
return found;
+ }
}
return nullptr;
@@ -87,8 +91,9 @@ void _get_directory_contents(EditorFileSystemDirectory *p_dir, PackedStringArray
Node *_try_find_owner_node_in_tree(const Ref<Script> p_script) {
SceneTree *tree = SceneTree::get_singleton();
- if (!tree)
+ if (!tree) {
return nullptr;
+ }
Node *base = tree->get_edited_scene_root();
if (base) {
base = _find_node_for_script(base, base, p_script);
@@ -107,8 +112,9 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
for (List<PropertyInfo>::Element *E = project_props.front(); E; E = E->next()) {
const PropertyInfo &prop = E->get();
- if (!prop.name.begins_with("input/"))
+ if (!prop.name.begins_with("input/")) {
continue;
+ }
String name = prop.name.substr(prop.name.find("/") + 1, prop.name.length());
suggestions.push_back(quoted(name));
@@ -117,16 +123,11 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
case CompletionKind::NODE_PATHS: {
{
// AutoLoads
- List<PropertyInfo> props;
- ProjectSettings::get_singleton()->get_property_list(&props);
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
- if (!s.begins_with("autoload/")) {
- continue;
- }
- String name = s.get_slice("/", 1);
- suggestions.push_back(quoted("/root/" + name));
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->value();
+ suggestions.push_back(quoted("/root/" + String(info.name)));
}
}
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index b183787618..68fc372959 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -324,7 +324,7 @@ MonoObject *godot_icall_Internal_GetScriptsMetadataOrNothing(MonoReflectionType
MonoType *dict_type = mono_reflection_type_get_type(p_dict_reftype);
- uint32_t type_encoding = mono_type_get_type(dict_type);
+ int type_encoding = mono_type_get_type(dict_type);
MonoClass *type_class_raw = mono_class_from_mono_type(dict_type);
GDMonoClass *type_class = GDMono::get_singleton()->get_class(type_class_raw);
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 1cdb08d50e..2edd8c87dc 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -43,6 +43,16 @@
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;
@@ -67,7 +77,7 @@ AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
};
}
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
+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++) {
@@ -75,29 +85,20 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
const String &ref_name = ref_info.name;
- if (r_assembly_dependencies.has(ref_name))
+ if (r_assembly_dependencies.has(ref_name)) {
continue;
+ }
- GDMonoAssembly *ref_assembly = nullptr;
-
- {
- MonoAssemblyName *ref_aname = mono_assembly_name_new("A"); // We can't allocate an empty MonoAssemblyName, hence "A"
- CRASH_COND(ref_aname == nullptr);
- SCOPE_EXIT {
- mono_assembly_name_free(ref_aname);
- mono_free(ref_aname);
- };
-
- mono_assembly_get_assemblyref(image, i, ref_aname);
-
- if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
- ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
- }
+ mono_assembly_get_assemblyref(image, i, reusable_aname);
- r_assembly_dependencies[ref_name] = ref_assembly->get_path();
+ GDMonoAssembly *ref_assembly = NULL;
+ 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 + "'.");
}
- Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies);
+ 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 + "'.");
}
@@ -129,9 +130,13 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
- Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies);
- if (err != OK)
+ 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;
diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
index 7276612230..f7d6e7e302 100644
--- a/modules/mono/editor/script_class_parser.cpp
+++ b/modules/mono/editor/script_class_parser.cpp
@@ -151,7 +151,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
case '"': {
bool verbatim = idx != 0 && code[idx - 1] == '@';
- CharType begin_str = code[idx];
+ char32_t begin_str = code[idx];
idx++;
String tk_string = String();
while (true) {
@@ -170,13 +170,13 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
} else if (code[idx] == '\\' && !verbatim) {
//escaped characters...
idx++;
- CharType next = code[idx];
+ char32_t next = code[idx];
if (next == 0) {
error_str = "Unterminated String";
error = true;
return TK_ERROR;
}
- CharType res = 0;
+ char32_t res = 0;
switch (next) {
case 'b':
@@ -208,8 +208,9 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
tk_string += res;
} else {
- if (code[idx] == '\n')
+ if (code[idx] == '\n') {
line++;
+ }
tk_string += code[idx];
}
idx++;
@@ -233,8 +234,8 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) {
//a number
- const CharType *rptr;
- double number = String::to_double(&code[idx], &rptr);
+ const char32_t *rptr;
+ double number = String::to_float(&code[idx], &rptr);
idx += (rptr - &code[idx]);
value = number;
return TK_NUMBER;
@@ -300,15 +301,17 @@ Error ScriptClassParser::_skip_generic_type_params() {
tk = get_token();
- if (tk != TK_PERIOD)
+ if (tk != TK_PERIOD) {
break;
+ }
}
}
if (tk == TK_OP_LESS) {
Error err = _skip_generic_type_params();
- if (err)
+ if (err) {
return err;
+ }
tk = get_token();
}
@@ -349,12 +352,14 @@ Error ScriptClassParser::_parse_type_full_name(String &r_full_name) {
// We don't mind if the base is generic, but we skip it any ways since this information is not needed
Error err = _skip_generic_type_params();
- if (err)
+ if (err) {
return err;
+ }
}
- if (code[idx] != '.') // We only want to take the next token if it's a period
+ if (code[idx] != '.') { // We only want to take the next token if it's a period
return OK;
+ }
tk = get_token();
@@ -369,15 +374,17 @@ Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) {
String name;
Error err = _parse_type_full_name(name);
- if (err)
+ if (err) {
return err;
+ }
Token tk = get_token();
if (tk == TK_COMMA) {
err = _parse_class_base(r_base);
- if (err)
+ if (err) {
return err;
+ }
} else if (tk == TK_IDENTIFIER && String(value) == "where") {
err = _parse_type_constraints();
if (err) {
@@ -433,8 +440,9 @@ Error ScriptClassParser::_parse_type_constraints() {
tk = get_token();
- if (tk != TK_PERIOD)
+ if (tk != TK_PERIOD) {
break;
+ }
}
}
}
@@ -452,8 +460,9 @@ Error ScriptClassParser::_parse_type_constraints() {
}
} else if (tk == TK_OP_LESS) {
Error err = _skip_generic_type_params();
- if (err)
+ if (err) {
return err;
+ }
} else if (tk == TK_CURLY_BRACKET_OPEN) {
return OK;
} else {
@@ -522,8 +531,9 @@ Error ScriptClassParser::parse(const String &p_code) {
const NameDecl &name_decl = E->value();
if (name_decl.type == NameDecl::NAMESPACE_DECL) {
- if (E != name_stack.front())
+ if (E != name_stack.front()) {
class_decl.namespace_ += ".";
+ }
class_decl.namespace_ += name_decl.name;
} else {
class_decl.name += name_decl.name + ".";
@@ -540,8 +550,9 @@ Error ScriptClassParser::parse(const String &p_code) {
if (tk == TK_COLON) {
Error err = _parse_class_base(class_decl.base);
- if (err)
+ if (err) {
return err;
+ }
curly_stack++;
type_curly_stack++;
@@ -555,8 +566,9 @@ Error ScriptClassParser::parse(const String &p_code) {
generic = true;
Error err = _skip_generic_type_params();
- if (err)
+ if (err) {
return err;
+ }
} else if (tk == TK_IDENTIFIER && String(value) == "where") {
Error err = _parse_type_constraints();
if (err) {
@@ -584,8 +596,9 @@ Error ScriptClassParser::parse(const String &p_code) {
classes.push_back(class_decl);
} else if (OS::get_singleton()->is_stdout_verbose()) {
String full_name = class_decl.namespace_;
- if (full_name.length())
+ if (full_name.length()) {
full_name += ".";
+ }
full_name += class_decl.name;
OS::get_singleton()->print("Ignoring generic class declaration: %s\n", full_name.utf8().get_data());
}
@@ -602,8 +615,9 @@ Error ScriptClassParser::parse(const String &p_code) {
int at_level = curly_stack;
Error err = _parse_namespace_name(name, curly_stack);
- if (err)
+ if (err) {
return err;
+ }
NameDecl name_decl;
name_decl.name = name;
@@ -614,8 +628,9 @@ Error ScriptClassParser::parse(const String &p_code) {
} else if (tk == TK_CURLY_BRACKET_CLOSE) {
curly_stack--;
if (name_stack.has(curly_stack)) {
- if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL)
+ if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL) {
type_curly_stack--;
+ }
name_stack.erase(curly_stack);
}
}
@@ -628,8 +643,9 @@ Error ScriptClassParser::parse(const String &p_code) {
error = true;
}
- if (error)
+ if (error) {
return ERR_PARSE_ERROR;
+ }
return OK;
}
@@ -702,8 +718,9 @@ static void run_dummy_preprocessor(String &r_source, const String &p_filepath) {
// Custom join ignoring lines removed by the preprocessor
for (int i = 0; i < lines.size(); i++) {
- if (i > 0 && include_lines[i - 1])
+ if (i > 0 && include_lines[i - 1]) {
r_source += '\n';
+ }
if (include_lines[i]) {
r_source += lines[i];
diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h
index c194ed1422..d611e8fb74 100644
--- a/modules/mono/editor/script_class_parser.h
+++ b/modules/mono/editor/script_class_parser.h
@@ -53,7 +53,6 @@ public:
String namespace_;
Vector<String> base;
bool nested;
- bool has_script_attr;
};
private:
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index 6a4f785551..3aecce50f5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -14,6 +14,10 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// Axis-Aligned Bounding Box. AABB consists of a position, a size, and
+ /// several utility functions. It is typically used for fast overlap tests.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct AABB : IEquatable<AABB>
@@ -21,24 +25,55 @@ namespace Godot
private Vector3 _position;
private Vector3 _size;
+ /// <summary>
+ /// Beginning corner. Typically has values lower than End.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector3 Position
{
get { return _position; }
set { _position = value; }
}
+ /// <summary>
+ /// Size from Position to End. Typically all components are positive.
+ /// If the size is negative, you can use <see cref="Abs"/> to fix it.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector3 Size
{
get { return _size; }
set { _size = value; }
}
+ /// <summary>
+ /// Ending corner. This is calculated as <see cref="Position"/> plus
+ /// <see cref="Size"/>. Setting this value will change the size.
+ /// </summary>
+ /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
public Vector3 End
{
get { return _position + _size; }
set { _size = value - _position; }
}
+ /// <summary>
+ /// Returns an AABB with equivalent position and size, modified so that
+ /// the most-negative corner is the origin and the size is positive.
+ /// </summary>
+ /// <returns>The modified AABB.</returns>
+ public AABB Abs()
+ {
+ Vector3 end = End;
+ Vector3 topLeft = new Vector3(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y), Mathf.Min(_position.z, end.z));
+ return new AABB(topLeft, _size.Abs());
+ }
+
+ /// <summary>
+ /// Returns true if this AABB completely encloses another one.
+ /// </summary>
+ /// <param name="with">The other AABB that may be enclosed.</param>
+ /// <returns>A bool for whether or not this AABB encloses `b`.</returns>
public bool Encloses(AABB with)
{
Vector3 src_min = _position;
@@ -54,33 +89,59 @@ namespace Godot
src_max.z > dst_max.z;
}
+ /// <summary>
+ /// Returns this AABB expanded to include a given point.
+ /// </summary>
+ /// <param name="point">The point to include.</param>
+ /// <returns>The expanded AABB.</returns>
public AABB Expand(Vector3 point)
{
Vector3 begin = _position;
Vector3 end = _position + _size;
if (point.x < begin.x)
+ {
begin.x = point.x;
+ }
if (point.y < begin.y)
+ {
begin.y = point.y;
+ }
if (point.z < begin.z)
+ {
begin.z = point.z;
+ }
if (point.x > end.x)
+ {
end.x = point.x;
+ }
if (point.y > end.y)
+ {
end.y = point.y;
+ }
if (point.z > end.z)
+ {
end.z = point.z;
+ }
return new AABB(begin, end - begin);
}
+ /// <summary>
+ /// Returns the area of the AABB.
+ /// </summary>
+ /// <returns>The area.</returns>
public real_t GetArea()
{
return _size.x * _size.y * _size.z;
}
+ /// <summary>
+ /// Gets the position of one of the 8 endpoints of the AABB.
+ /// </summary>
+ /// <param name="idx">Which endpoint to get.</param>
+ /// <returns>An endpoint of the AABB.</returns>
public Vector3 GetEndpoint(int idx)
{
switch (idx)
@@ -106,6 +167,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the normalized longest axis of the AABB.
+ /// </summary>
+ /// <returns>A vector representing the normalized longest axis of the AABB.</returns>
public Vector3 GetLongestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
@@ -125,6 +190,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the AABB.
+ /// </summary>
+ /// <returns>A <see cref="Vector3.Axis"/> index for which axis is longest.</returns>
public Vector3.Axis GetLongestAxisIndex()
{
var axis = Vector3.Axis.X;
@@ -144,6 +213,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the scalar length of the longest axis of the AABB.
+ /// </summary>
+ /// <returns>The scalar length of the longest axis of the AABB.</returns>
public real_t GetLongestAxisSize()
{
real_t max_size = _size.x;
@@ -157,6 +230,10 @@ namespace Godot
return max_size;
}
+ /// <summary>
+ /// Returns the normalized shortest axis of the AABB.
+ /// </summary>
+ /// <returns>A vector representing the normalized shortest axis of the AABB.</returns>
public Vector3 GetShortestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
@@ -176,6 +253,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the AABB.
+ /// </summary>
+ /// <returns>A <see cref="Vector3.Axis"/> index for which axis is shortest.</returns>
public Vector3.Axis GetShortestAxisIndex()
{
var axis = Vector3.Axis.X;
@@ -195,6 +276,10 @@ namespace Godot
return axis;
}
+ /// <summary>
+ /// Returns the scalar length of the shortest axis of the AABB.
+ /// </summary>
+ /// <returns>The scalar length of the shortest axis of the AABB.</returns>
public real_t GetShortestAxisSize()
{
real_t max_size = _size.x;
@@ -208,6 +293,12 @@ namespace Godot
return max_size;
}
+ /// <summary>
+ /// Returns the support point in a given direction.
+ /// This is useful for collision detection algorithms.
+ /// </summary>
+ /// <param name="dir">The direction to find support for.</param>
+ /// <returns>A vector representing the support.</returns>
public Vector3 GetSupport(Vector3 dir)
{
Vector3 half_extents = _size * 0.5f;
@@ -219,6 +310,11 @@ namespace Godot
dir.z > 0f ? -half_extents.z : half_extents.z);
}
+ /// <summary>
+ /// Returns a copy of the AABB grown a given amount of units towards all the sides.
+ /// </summary>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown AABB.</returns>
public AABB Grow(real_t by)
{
var res = this;
@@ -233,16 +329,29 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns true if the AABB is flat or empty, or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the AABB has area.</returns>
public bool HasNoArea()
{
return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f;
}
+ /// <summary>
+ /// Returns true if the AABB has no surface (no size), or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the AABB has area.</returns>
public bool HasNoSurface()
{
return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f;
}
+ /// <summary>
+ /// Returns true if the AABB contains a point, or false otherwise.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the AABB contains `point`.</returns>
public bool HasPoint(Vector3 point)
{
if (point.x < _position.x)
@@ -261,6 +370,11 @@ namespace Godot
return true;
}
+ /// <summary>
+ /// Returns the intersection of this AABB and `b`.
+ /// </summary>
+ /// <param name="with">The other AABB.</param>
+ /// <returns>The clipped AABB.</returns>
public AABB Intersection(AABB with)
{
Vector3 src_min = _position;
@@ -297,24 +411,57 @@ namespace Godot
return new AABB(min, max - min);
}
- public bool Intersects(AABB with)
+ /// <summary>
+ /// Returns true if the AABB overlaps with `b`
+ /// (i.e. they have at least one point in common).
+ ///
+ /// If `includeBorders` is true, they will also be considered overlapping
+ /// if their borders touch, even without intersection.
+ /// </summary>
+ /// <param name="with">The other AABB to check for intersections with.</param>
+ /// <param name="includeBorders">Whether or not to consider borders.</param>
+ /// <returns>A bool for whether or not they are intersecting.</returns>
+ public bool Intersects(AABB with, bool includeBorders = false)
{
- if (_position.x >= with._position.x + with._size.x)
- return false;
- if (_position.x + _size.x <= with._position.x)
- return false;
- if (_position.y >= with._position.y + with._size.y)
- return false;
- if (_position.y + _size.y <= with._position.y)
- return false;
- if (_position.z >= with._position.z + with._size.z)
- return false;
- if (_position.z + _size.z <= with._position.z)
- return false;
+ if (includeBorders)
+ {
+ if (_position.x > with._position.x + with._size.x)
+ return false;
+ if (_position.x + _size.x < with._position.x)
+ return false;
+ if (_position.y > with._position.y + with._size.y)
+ return false;
+ if (_position.y + _size.y < with._position.y)
+ return false;
+ if (_position.z > with._position.z + with._size.z)
+ return false;
+ if (_position.z + _size.z < with._position.z)
+ return false;
+ }
+ else
+ {
+ if (_position.x >= with._position.x + with._size.x)
+ return false;
+ if (_position.x + _size.x <= with._position.x)
+ return false;
+ if (_position.y >= with._position.y + with._size.y)
+ return false;
+ if (_position.y + _size.y <= with._position.y)
+ return false;
+ if (_position.z >= with._position.z + with._size.z)
+ return false;
+ if (_position.z + _size.z <= with._position.z)
+ return false;
+ }
return true;
}
+ /// <summary>
+ /// Returns true if the AABB is on both sides of `plane`.
+ /// </summary>
+ /// <param name="plane">The plane to check for intersection.</param>
+ /// <returns>A bool for whether or not the AABB intersects the plane.</returns>
public bool IntersectsPlane(Plane plane)
{
Vector3[] points =
@@ -335,14 +482,24 @@ namespace Godot
for (int i = 0; i < 8; i++)
{
if (plane.DistanceTo(points[i]) > 0)
+ {
over = true;
+ }
else
+ {
under = true;
+ }
}
return under && over;
}
+ /// <summary>
+ /// Returns true if the AABB intersects the line segment between `from` and `to`.
+ /// </summary>
+ /// <param name="from">The start of the line segment.</param>
+ /// <param name="to">The end of the line segment.</param>
+ /// <returns>A bool for whether or not the AABB intersects the line segment.</returns>
public bool IntersectsSegment(Vector3 from, Vector3 to)
{
real_t min = 0f;
@@ -359,7 +516,9 @@ namespace Godot
if (segFrom < segTo)
{
if (segFrom > boxEnd || segTo < boxBegin)
+ {
return false;
+ }
real_t length = segTo - segFrom;
cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f;
@@ -368,7 +527,9 @@ namespace Godot
else
{
if (segTo > boxEnd || segFrom < boxBegin)
+ {
return false;
+ }
real_t length = segTo - segFrom;
cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f;
@@ -381,14 +542,23 @@ namespace Godot
}
if (cmax < max)
+ {
max = cmax;
+ }
if (max < min)
+ {
return false;
+ }
}
return true;
}
+ /// <summary>
+ /// Returns a larger AABB that contains this AABB and `b`.
+ /// </summary>
+ /// <param name="with">The other AABB.</param>
+ /// <returns>The merged AABB.</returns>
public AABB Merge(AABB with)
{
Vector3 beg1 = _position;
@@ -411,22 +581,52 @@ namespace Godot
return new AABB(min, max - min);
}
- // Constructors
+ /// <summary>
+ /// Constructs an AABB from a position and size.
+ /// </summary>
+ /// <param name="position">The position.</param>
+ /// <param name="size">The size, typically positive.</param>
public AABB(Vector3 position, Vector3 size)
{
_position = position;
_size = size;
}
+
+ /// <summary>
+ /// Constructs an AABB from a position, width, height, and depth.
+ /// </summary>
+ /// <param name="position">The position.</param>
+ /// <param name="width">The width, typically positive.</param>
+ /// <param name="height">The height, typically positive.</param>
+ /// <param name="depth">The depth, typically positive.</param>
public AABB(Vector3 position, real_t width, real_t height, real_t depth)
{
_position = position;
_size = new Vector3(width, height, depth);
}
+
+ /// <summary>
+ /// Constructs an AABB from x, y, z, and size.
+ /// </summary>
+ /// <param name="x">The position's X coordinate.</param>
+ /// <param name="y">The position's Y coordinate.</param>
+ /// <param name="z">The position's Z coordinate.</param>
+ /// <param name="size">The size, typically positive.</param>
public AABB(real_t x, real_t y, real_t z, Vector3 size)
{
_position = new Vector3(x, y, z);
_size = size;
}
+
+ /// <summary>
+ /// Constructs an AABB from x, y, z, width, height, and depth.
+ /// </summary>
+ /// <param name="x">The position's X coordinate.</param>
+ /// <param name="y">The position's Y coordinate.</param>
+ /// <param name="z">The position's Z coordinate.</param>
+ /// <param name="width">The width, typically positive.</param>
+ /// <param name="height">The height, typically positive.</param>
+ /// <param name="depth">The depth, typically positive.</param>
public AABB(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth)
{
_position = new Vector3(x, y, z);
@@ -458,6 +658,12 @@ namespace Godot
return _position == other._position && _size == other._size;
}
+ /// <summary>
+ /// Returns true if this AABB and `other` are approximately equal, by running
+ /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other AABB to compare.</param>
+ /// <returns>Whether or not the AABBs are approximately equal.</returns>
public bool IsEqualApprox(AABB other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index a963810d63..f77d3052f4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -44,6 +44,15 @@ namespace Godot.Collections
Add(element);
}
+ public Array(params object[] array) : this()
+ {
+ if (array == null)
+ {
+ throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
+ }
+ safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
+ }
+
internal Array(ArraySafeHandle handle)
{
safeHandle = handle;
@@ -72,6 +81,11 @@ namespace Godot.Collections
return godot_icall_Array_Resize(GetPtr(), newSize);
}
+ public static Array operator +(Array left, Array right)
+ {
+ return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr()));
+ }
+
// IDisposable
public void Dispose()
@@ -155,6 +169,9 @@ namespace Godot.Collections
internal extern static IntPtr godot_icall_Array_Ctor();
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -176,6 +193,9 @@ namespace Godot.Collections
internal extern static void godot_icall_Array_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -231,6 +251,15 @@ namespace Godot.Collections
objectArray = new Array(collection);
}
+ public Array(params T[] array) : this()
+ {
+ if (array == null)
+ {
+ throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
+ }
+ objectArray = new Array(array);
+ }
+
public Array(Array array)
{
objectArray = array;
@@ -266,6 +295,11 @@ namespace Godot.Collections
return objectArray.Resize(newSize);
}
+ public static Array<T> operator +(Array<T> left, Array<T> right)
+ {
+ return new Array<T>(left.objectArray + right.objectArray);
+ }
+
// IList<T>
public T this[int index]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index baf470a0cc..3f1120575f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -8,6 +8,20 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// 3×3 matrix used for 3D rotation and scale.
+ /// Almost always used as an orthogonal basis for a Transform.
+ ///
+ /// Contains 3 vector fields X, Y and Z as its columns, which are typically
+ /// interpreted as the local basis vectors of a 3D transformation. For such use,
+ /// it is composed of a scaling and a rotation matrix, in that order (M = R.S).
+ ///
+ /// Can also be accessed as array of 3D vectors. These vectors are normally
+ /// orthogonal to each other, but are not necessarily normalized (due to scaling).
+ ///
+ /// For more information, read this documentation article:
+ /// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Basis : IEquatable<Basis>
@@ -15,9 +29,9 @@ namespace Godot
// NOTE: x, y and z are public-only. Use Column0, Column1 and Column2 internally.
/// <summary>
- /// Returns the basis matrix’s x vector.
- /// This is equivalent to <see cref="Column0"/>.
+ /// The basis matrix's X vector (column 0).
/// </summary>
+ /// <value>Equivalent to <see cref="Column0"/> and array index `[0]`.</value>
public Vector3 x
{
get => Column0;
@@ -25,9 +39,9 @@ namespace Godot
}
/// <summary>
- /// Returns the basis matrix’s y vector.
- /// This is equivalent to <see cref="Column1"/>.
+ /// The basis matrix's Y vector (column 1).
/// </summary>
+ /// <value>Equivalent to <see cref="Column1"/> and array index `[1]`.</value>
public Vector3 y
{
get => Column1;
@@ -35,19 +49,40 @@ namespace Godot
}
/// <summary>
- /// Returns the basis matrix’s z vector.
- /// This is equivalent to <see cref="Column2"/>.
+ /// The basis matrix's Z vector (column 2).
/// </summary>
+ /// <value>Equivalent to <see cref="Column2"/> and array index `[2]`.</value>
public Vector3 z
{
get => Column2;
set => Column2 = value;
}
+ /// <summary>
+ /// Row 0 of the basis matrix. Shows which vectors contribute
+ /// to the X direction. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
public Vector3 Row0;
+
+ /// <summary>
+ /// Row 1 of the basis matrix. Shows which vectors contribute
+ /// to the Y direction. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
public Vector3 Row1;
+
+ /// <summary>
+ /// Row 2 of the basis matrix. Shows which vectors contribute
+ /// to the Z direction. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
public Vector3 Row2;
+ /// <summary>
+ /// Column 0 of the basis matrix (the X vector).
+ /// </summary>
+ /// <value>Equivalent to <see cref="x"/> and array index `[0]`.</value>
public Vector3 Column0
{
get => new Vector3(Row0.x, Row1.x, Row2.x);
@@ -58,6 +93,11 @@ namespace Godot
this.Row2.x = value.z;
}
}
+
+ /// <summary>
+ /// Column 1 of the basis matrix (the Y vector).
+ /// </summary>
+ /// <value>Equivalent to <see cref="y"/> and array index `[1]`.</value>
public Vector3 Column1
{
get => new Vector3(Row0.y, Row1.y, Row2.y);
@@ -68,6 +108,11 @@ namespace Godot
this.Row2.y = value.z;
}
}
+
+ /// <summary>
+ /// Column 2 of the basis matrix (the Z vector).
+ /// </summary>
+ /// <value>Equivalent to <see cref="z"/> and array index `[2]`.</value>
public Vector3 Column2
{
get => new Vector3(Row0.z, Row1.z, Row2.z);
@@ -79,6 +124,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The scale of this basis.
+ /// </summary>
+ /// <value>Equivalent to the lengths of each column vector, but negative if the determinant is negative.</value>
public Vector3 Scale
{
get
@@ -86,11 +135,18 @@ namespace Godot
real_t detSign = Mathf.Sign(Determinant());
return detSign * new Vector3
(
- new Vector3(this.Row0[0], this.Row1[0], this.Row2[0]).Length(),
- new Vector3(this.Row0[1], this.Row1[1], this.Row2[1]).Length(),
- new Vector3(this.Row0[2], this.Row1[2], this.Row2[2]).Length()
+ Column0.Length(),
+ Column1.Length(),
+ Column2.Length()
);
}
+ set
+ {
+ value /= Scale; // Value becomes what's called "delta_scale" in core.
+ Column0 *= value.x;
+ Column1 *= value.y;
+ Column2 *= value.z;
+ }
}
/// <summary>
@@ -151,14 +207,15 @@ namespace Godot
}
}
- internal Quat RotationQuat()
+ public Quat RotationQuat()
{
Basis orthonormalizedBasis = Orthonormalized();
real_t det = orthonormalizedBasis.Determinant();
if (det < 0)
{
- // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles.
- orthonormalizedBasis = orthonormalizedBasis.Scaled(Vector3.NegOne);
+ // Ensure that the determinant is 1, such that result is a proper
+ // rotation matrix which can be represented by Euler angles.
+ orthonormalizedBasis = orthonormalizedBasis.Scaled(-Vector3.One);
}
return orthonormalizedBasis.Quat();
@@ -182,6 +239,15 @@ namespace Godot
Row2 = new Vector3(0, 0, diagonal.z);
}
+ /// <summary>
+ /// Returns the determinant of the basis matrix. If the basis is
+ /// uniformly scaled, its determinant is the square of the scale.
+ ///
+ /// A negative determinant means the basis has a negative scale.
+ /// A zero determinant means the basis isn't invertible,
+ /// and is usually considered invalid.
+ /// </summary>
+ /// <returns>The determinant of the basis matrix.</returns>
public real_t Determinant()
{
real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
@@ -191,6 +257,16 @@ namespace Godot
return Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20;
}
+ /// <summary>
+ /// Returns the basis's rotation in the form of Euler angles
+ /// (in the YXZ convention: when *decomposing*, first Z, then X, and Y last).
+ /// The returned vector contains the rotation angles in
+ /// the format (X angle, Y angle, Z angle).
+ ///
+ /// Consider using the <see cref="Basis.Quat()"/> method instead, which
+ /// returns a <see cref="Godot.Quat"/> quaternion instead of Euler angles.
+ /// </summary>
+ /// <returns>A Vector3 representing the basis rotation in Euler angles.</returns>
public Vector3 GetEuler()
{
Basis m = Orthonormalized();
@@ -223,6 +299,12 @@ namespace Godot
return euler;
}
+ /// <summary>
+ /// Get rows by index. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
+ /// <param name="index">Which row.</param>
+ /// <returns>One of `Row0`, `Row1`, or `Row2`.</returns>
public Vector3 GetRow(int index)
{
switch (index)
@@ -238,6 +320,12 @@ namespace Godot
}
}
+ /// <summary>
+ /// Sets rows by index. Rows are not very useful for user code,
+ /// but are more efficient for some internal calculations.
+ /// </summary>
+ /// <param name="index">Which row.</param>
+ /// <param name="value">The vector to set the row to.</param>
public void SetRow(int index, Vector3 value)
{
switch (index)
@@ -256,16 +344,16 @@ namespace Godot
}
}
- public Vector3 GetColumn(int index)
- {
- return this[index];
- }
-
- public void SetColumn(int index, Vector3 value)
- {
- this[index] = value;
- }
-
+ /// <summary>
+ /// This function considers a discretization of rotations into
+ /// 24 points on unit sphere, lying along the vectors (x, y, z) with
+ /// each component being either -1, 0, or 1, and returns the index
+ /// of the point best representing the orientation of the object.
+ /// It is mainly used by the <see cref="GridMap"/> editor.
+ ///
+ /// For further details, refer to the Godot source code.
+ /// </summary>
+ /// <returns>The orthogonal index.</returns>
public int GetOrthogonalIndex()
{
var orth = this;
@@ -279,11 +367,17 @@ namespace Godot
real_t v = row[j];
if (v > 0.5f)
+ {
v = 1.0f;
+ }
else if (v < -0.5f)
+ {
v = -1.0f;
+ }
else
+ {
v = 0f;
+ }
row[j] = v;
@@ -294,12 +388,18 @@ namespace Godot
for (int i = 0; i < 24; i++)
{
if (orth == _orthoBases[i])
+ {
return i;
+ }
}
return 0;
}
+ /// <summary>
+ /// Returns the inverse of the matrix.
+ /// </summary>
+ /// <returns>The inverse matrix.</returns>
public Basis Inverse()
{
real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
@@ -309,7 +409,9 @@ namespace Godot
real_t det = Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20;
if (det == 0)
+ {
throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
+ }
real_t detInv = 1.0f / det;
@@ -328,11 +430,17 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the orthonormalized version of the basis matrix (useful to
+ /// call occasionally to avoid rounding errors for orthogonal matrices).
+ /// This performs a Gram-Schmidt orthonormalization on the basis of the matrix.
+ /// </summary>
+ /// <returns>An orthonormalized basis matrix.</returns>
public Basis Orthonormalized()
{
- Vector3 column0 = GetColumn(0);
- Vector3 column1 = GetColumn(1);
- Vector3 column2 = GetColumn(2);
+ Vector3 column0 = this[0];
+ Vector3 column1 = this[1];
+ Vector3 column2 = this[2];
column0.Normalize();
column1 = column1 - column0 * column0.Dot(column1);
@@ -343,48 +451,86 @@ namespace Godot
return new Basis(column0, column1, column2);
}
+ /// <summary>
+ /// Introduce an additional rotation around the given `axis`
+ /// by `phi` (in radians). The axis must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <returns>The rotated basis matrix.</returns>
public Basis Rotated(Vector3 axis, real_t phi)
{
return new Basis(axis, phi) * this;
}
+ /// <summary>
+ /// Introduce an additional scaling specified by the given 3D scaling factor.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled basis matrix.</returns>
public Basis Scaled(Vector3 scale)
{
- var b = this;
+ Basis b = this;
b.Row0 *= scale.x;
b.Row1 *= scale.y;
b.Row2 *= scale.z;
return b;
}
- public Basis Slerp(Basis target, real_t t)
+ /// <summary>
+ /// Assuming that the matrix is a proper rotation matrix, slerp performs
+ /// a spherical-linear interpolation with another rotation matrix.
+ /// </summary>
+ /// <param name="target">The destination basis for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting basis matrix of the interpolation.</returns>
+ public Basis Slerp(Basis target, real_t weight)
{
- var from = new Quat(this);
- var to = new Quat(target);
+ Quat from = new Quat(this);
+ Quat to = new Quat(target);
- var b = new Basis(from.Slerp(to, t));
- b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), t);
- b.Row1 *= Mathf.Lerp(Row1.Length(), target.Row1.Length(), t);
- b.Row2 *= Mathf.Lerp(Row2.Length(), target.Row2.Length(), t);
+ Basis b = new Basis(from.Slerp(to, weight));
+ b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), weight);
+ b.Row1 *= Mathf.Lerp(Row1.Length(), target.Row1.Length(), weight);
+ b.Row2 *= Mathf.Lerp(Row2.Length(), target.Row2.Length(), weight);
return b;
}
+ /// <summary>
+ /// Transposed dot product with the X axis of the matrix.
+ /// </summary>
+ /// <param name="with">A vector to calculate the dot product with.</param>
+ /// <returns>The resulting dot product.</returns>
public real_t Tdotx(Vector3 with)
{
return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2];
}
+ /// <summary>
+ /// Transposed dot product with the Y axis of the matrix.
+ /// </summary>
+ /// <param name="with">A vector to calculate the dot product with.</param>
+ /// <returns>The resulting dot product.</returns>
public real_t Tdoty(Vector3 with)
{
return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2];
}
+ /// <summary>
+ /// Transposed dot product with the Z axis of the matrix.
+ /// </summary>
+ /// <param name="with">A vector to calculate the dot product with.</param>
+ /// <returns>The resulting dot product.</returns>
public real_t Tdotz(Vector3 with)
{
return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2];
}
+ /// <summary>
+ /// Returns the transposed version of the basis matrix.
+ /// </summary>
+ /// <returns>The transposed basis matrix.</returns>
public Basis Transposed()
{
var tr = this;
@@ -404,6 +550,11 @@ namespace Godot
return tr;
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the basis matrix.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector3 Xform(Vector3 v)
{
return new Vector3
@@ -414,6 +565,14 @@ namespace Godot
);
}
+ /// <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>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector3 XformInv(Vector3 v)
{
return new Vector3
@@ -424,6 +583,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the basis's rotation in the form of a quaternion.
+ /// See <see cref="GetEuler()"/> if you need Euler angles, but keep in
+ /// mind that quaternions should generally be preferred to Euler angles.
+ /// </summary>
+ /// <returns>A <see cref="Godot.Quat"/> representing the basis's rotation.</returns>
public Quat Quat()
{
real_t trace = Row0[0] + Row1[1] + Row2[2];
@@ -508,11 +673,33 @@ namespace Godot
private static readonly Basis _flipY = new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1);
private static readonly Basis _flipZ = new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1);
+ /// <summary>
+ /// The identity basis, with no rotation or scaling applied.
+ /// This is used as a replacement for `Basis()` in GDScript.
+ /// Do not use `new Basis()` with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Back)`.</value>
public static Basis Identity { get { return _identity; } }
+ /// <summary>
+ /// The basis that will flip something along the X axis when used in a transformation.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Left, Vector3.Up, Vector3.Back)`.</value>
public static Basis FlipX { get { return _flipX; } }
+ /// <summary>
+ /// The basis that will flip something along the Y axis when used in a transformation.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Down, Vector3.Back)`.</value>
public static Basis FlipY { get { return _flipY; } }
+ /// <summary>
+ /// The basis that will flip something along the Z axis when used in a transformation.
+ /// </summary>
+ /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Forward)`.</value>
public static Basis FlipZ { get { return _flipZ; } }
+ /// <summary>
+ /// Constructs a pure rotation basis matrix from the given quaternion.
+ /// </summary>
+ /// <param name="quat">The quaternion to create the basis from.</param>
public Basis(Quat quat)
{
real_t s = 2.0f / quat.LengthSquared;
@@ -535,26 +722,41 @@ namespace Godot
Row2 = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
}
- public Basis(Vector3 euler)
+ /// <summary>
+ /// Constructs a pure rotation basis matrix from the given Euler angles
+ /// (in the YXZ convention: when *composing*, first Y, then X, and Z last),
+ /// given in the vector format as (X angle, Y angle, Z angle).
+ ///
+ /// Consider using the <see cref="Basis(Quat)"/> constructor instead, which
+ /// uses a <see cref="Godot.Quat"/> quaternion instead of Euler angles.
+ /// </summary>
+ /// <param name="eulerYXZ">The Euler angles to create the basis from.</param>
+ public Basis(Vector3 eulerYXZ)
{
real_t c;
real_t s;
- c = Mathf.Cos(euler.x);
- s = Mathf.Sin(euler.x);
+ c = Mathf.Cos(eulerYXZ.x);
+ s = Mathf.Sin(eulerYXZ.x);
var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c);
- c = Mathf.Cos(euler.y);
- s = Mathf.Sin(euler.y);
+ c = Mathf.Cos(eulerYXZ.y);
+ s = Mathf.Sin(eulerYXZ.y);
var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c);
- c = Mathf.Cos(euler.z);
- s = Mathf.Sin(euler.z);
+ c = Mathf.Cos(eulerYXZ.z);
+ s = Mathf.Sin(eulerYXZ.z);
var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1);
this = ymat * xmat * zmat;
}
+ /// <summary>
+ /// Constructs a pure rotation basis matrix, rotated around the given `axis`
+ /// by `phi` (in radians). The axis must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate, in radians.</param>
public Basis(Vector3 axis, real_t phi)
{
Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
@@ -582,6 +784,12 @@ namespace Godot
Row2.y = xyzt + zyxs;
}
+ /// <summary>
+ /// Constructs a basis matrix from 3 axis vectors (matrix columns).
+ /// </summary>
+ /// <param name="column0">The X vector, or Column0.</param>
+ /// <param name="column1">The Y vector, or Column1.</param>
+ /// <param name="column2">The Z vector, or Column2.</param>
public Basis(Vector3 column0, Vector3 column1, Vector3 column2)
{
Row0 = new Vector3(column0.x, column1.x, column2.x);
@@ -637,6 +845,12 @@ namespace Godot
return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2);
}
+ /// <summary>
+ /// Returns true if this basis and `other` are approximately equal, by running
+ /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other basis to compare.</param>
+ /// <returns>Whether or not the matrices are approximately equal.</returns>
public bool IsEqualApprox(Basis other)
{
return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index 6030b72a44..3700a6194f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -3,15 +3,44 @@ using System.Runtime.InteropServices;
namespace Godot
{
+ /// <summary>
+ /// A color represented by red, green, blue, and alpha (RGBA) components.
+ /// The alpha component is often used for transparency.
+ /// Values are in floating-point and usually range from 0 to 1.
+ /// Some properties (such as CanvasItem.modulate) may accept values
+ /// greater than 1 (overbright or HDR colors).
+ ///
+ /// If you want to supply values in a range of 0 to 255, you should use
+ /// <see cref="Color8"/> and the `r8`/`g8`/`b8`/`a8` properties.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Color : IEquatable<Color>
{
+ /// <summary>
+ /// The color's red component, typically on the range of 0 to 1.
+ /// </summary>
public float r;
+
+ /// <summary>
+ /// The color's green component, typically on the range of 0 to 1.
+ /// </summary>
public float g;
+
+ /// <summary>
+ /// The color's blue component, typically on the range of 0 to 1.
+ /// </summary>
public float b;
+
+ /// <summary>
+ /// The color's alpha (transparency) component, typically on the range of 0 to 1.
+ /// </summary>
public float a;
+ /// <summary>
+ /// Wrapper for <see cref="r"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int r8
{
get
@@ -24,6 +53,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Wrapper for <see cref="g"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int g8
{
get
@@ -36,6 +69,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Wrapper for <see cref="b"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int b8
{
get
@@ -48,6 +85,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// Wrapper for <see cref="a"/> that uses the range 0 to 255 instead of 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int a8
{
get
@@ -60,6 +101,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The HSV hue of this color, on the range 0 to 1.
+ /// </summary>
+ /// <value>Getting is a long process, refer to the source code for details. Setting uses <see cref="FromHsv"/>.</value>
public float h
{
get
@@ -70,21 +115,31 @@ namespace Godot
float delta = max - min;
if (delta == 0)
+ {
return 0;
+ }
float h;
if (r == max)
+ {
h = (g - b) / delta; // Between yellow & magenta
+ }
else if (g == max)
+ {
h = 2 + (b - r) / delta; // Between cyan & yellow
+ }
else
+ {
h = 4 + (r - g) / delta; // Between magenta & cyan
+ }
h /= 6.0f;
if (h < 0)
+ {
h += 1.0f;
+ }
return h;
}
@@ -94,6 +149,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The HSV saturation of this color, on the range 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to the ratio between the min and max RGB value. Setting uses <see cref="FromHsv"/>.</value>
public float s
{
get
@@ -103,7 +162,7 @@ namespace Godot
float delta = max - min;
- return max != 0 ? delta / max : 0;
+ return max == 0 ? 0 : delta / max;
}
set
{
@@ -111,6 +170,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The HSV value (brightness) of this color, on the range 0 to 1.
+ /// </summary>
+ /// <value>Getting is equivalent to using `Max()` on the RGB components. Setting uses <see cref="FromHsv"/>.</value>
public float v
{
get
@@ -123,25 +186,10 @@ namespace Godot
}
}
- public static Color ColorN(string name, float alpha = 1f)
- {
- name = name.Replace(" ", String.Empty);
- name = name.Replace("-", String.Empty);
- name = name.Replace("_", String.Empty);
- name = name.Replace("'", String.Empty);
- name = name.Replace(".", String.Empty);
- name = name.ToLower();
-
- if (!Colors.namedColors.ContainsKey(name))
- {
- throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
- }
-
- Color color = Colors.namedColors[name];
- color.a = alpha;
- return color;
- }
-
+ /// <summary>
+ /// Access color components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.r`, `[1]` is equivalent to `.g`, `[2]` is equivalent to `.b`, `[3]` is equivalent to `.a`.</value>
public float this[int index]
{
get
@@ -182,73 +230,13 @@ namespace Godot
}
}
- public void ToHsv(out float hue, out float saturation, out float value)
- {
- float max = (float)Mathf.Max(r, Mathf.Max(g, b));
- float min = (float)Mathf.Min(r, Mathf.Min(g, b));
-
- float delta = max - min;
-
- if (delta == 0)
- {
- hue = 0;
- }
- else
- {
- if (r == max)
- hue = (g - b) / delta; // Between yellow & magenta
- else if (g == max)
- hue = 2 + (b - r) / delta; // Between cyan & yellow
- else
- hue = 4 + (r - g) / delta; // Between magenta & cyan
-
- hue /= 6.0f;
-
- if (hue < 0)
- hue += 1.0f;
- }
-
- saturation = max == 0 ? 0 : 1f - 1f * min / max;
- value = max;
- }
-
- public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f)
- {
- if (saturation == 0)
- {
- // acp_hromatic (grey)
- return new Color(value, value, value, alpha);
- }
-
- int i;
- float f, p, q, t;
-
- hue *= 6.0f;
- hue %= 6f;
- i = (int)hue;
-
- f = hue - i;
- p = value * (1 - saturation);
- q = value * (1 - saturation * f);
- t = value * (1 - saturation * (1 - f));
-
- switch (i)
- {
- case 0: // Red is the dominant color
- return new Color(value, t, p, alpha);
- case 1: // Green is the dominant color
- return new Color(q, value, p, alpha);
- case 2:
- return new Color(p, value, t, alpha);
- case 3: // Blue is the dominant color
- return new Color(p, q, value, alpha);
- case 4:
- return new Color(t, p, value, alpha);
- default: // (5) Red is the dominant color
- return new Color(value, p, q, alpha);
- }
- }
-
+ /// <summary>
+ /// Returns a new color resulting from blending this color over another.
+ /// If the color is opaque, the result is also opaque.
+ /// The second color may have a range of alpha values.
+ /// </summary>
+ /// <param name="over">The color to blend over.</param>
+ /// <returns>This color blended over `over`.</returns>
public Color Blend(Color over)
{
Color res;
@@ -268,6 +256,10 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns the most contrasting color.
+ /// </summary>
+ /// <returns>The most contrasting color</returns>
public Color Contrasted()
{
return new Color(
@@ -278,6 +270,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a new color resulting from making this color darker
+ /// by the specified ratio (on the range of 0 to 1).
+ /// </summary>
+ /// <param name="amount">The ratio to darken by.</param>
+ /// <returns>The darkened color.</returns>
public Color Darkened(float amount)
{
Color res = this;
@@ -287,6 +285,10 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns the inverted color: `(1 - r, 1 - g, 1 - b, a)`.
+ /// </summary>
+ /// <returns>The inverted color.</returns>
public Color Inverted()
{
return new Color(
@@ -297,6 +299,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a new color resulting from making this color lighter
+ /// by the specified ratio (on the range of 0 to 1).
+ /// </summary>
+ /// <param name="amount">The ratio to lighten by.</param>
+ /// <returns>The darkened color.</returns>
public Color Lightened(float amount)
{
Color res = this;
@@ -306,6 +314,13 @@ namespace Godot
return res;
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this color and `to` by amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination color for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting color of the interpolation.</returns>
public Color Lerp(Color to, float weight)
{
return new Color
@@ -317,6 +332,13 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this color and `to` by color amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination color for interpolation.</param>
+ /// <param name="weight">A color with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting color of the interpolation.</returns>
public Color Lerp(Color to, Color weight)
{
return new Color
@@ -328,6 +350,12 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the color's 32-bit integer in ABGR format
+ /// (each byte represents a component of the ABGR profile).
+ /// ABGR is the reversed version of the default format.
+ /// </summary>
+ /// <returns>A uint representing this color in ABGR32 format.</returns>
public uint ToAbgr32()
{
uint c = (byte)Math.Round(a * 255);
@@ -341,6 +369,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 64-bit integer in ABGR format
+ /// (each word represents a component of the ABGR profile).
+ /// ABGR is the reversed version of the default format.
+ /// </summary>
+ /// <returns>A ulong representing this color in ABGR64 format.</returns>
public ulong ToAbgr64()
{
ulong c = (ushort)Math.Round(a * 65535);
@@ -354,6 +388,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 32-bit integer in ARGB format
+ /// (each byte represents a component of the ARGB profile).
+ /// ARGB is more compatible with DirectX, but not used much in Godot.
+ /// </summary>
+ /// <returns>A uint representing this color in ARGB32 format.</returns>
public uint ToArgb32()
{
uint c = (byte)Math.Round(a * 255);
@@ -367,6 +407,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 64-bit integer in ARGB format
+ /// (each word represents a component of the ARGB profile).
+ /// ARGB is more compatible with DirectX, but not used much in Godot.
+ /// </summary>
+ /// <returns>A ulong representing this color in ARGB64 format.</returns>
public ulong ToArgb64()
{
ulong c = (ushort)Math.Round(a * 65535);
@@ -380,6 +426,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 32-bit integer in RGBA format
+ /// (each byte represents a component of the RGBA profile).
+ /// RGBA is Godot's default and recommended format.
+ /// </summary>
+ /// <returns>A uint representing this color in RGBA32 format.</returns>
public uint ToRgba32()
{
uint c = (byte)Math.Round(r * 255);
@@ -393,6 +445,12 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's 64-bit integer in RGBA format
+ /// (each word represents a component of the RGBA profile).
+ /// RGBA is Godot's default and recommended format.
+ /// </summary>
+ /// <returns>A ulong representing this color in RGBA64 format.</returns>
public ulong ToRgba64()
{
ulong c = (ushort)Math.Round(r * 65535);
@@ -406,6 +464,11 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the color's HTML hexadecimal color string in RGBA format.
+ /// </summary>
+ /// <param name="includeAlpha">Whether or not to include alpha. If false, the color is RGB instead of RGBA.</param>
+ /// <returns>A string for the HTML hexadecimal representation of this color.</returns>
public string ToHtml(bool includeAlpha = true)
{
var txt = string.Empty;
@@ -415,12 +478,20 @@ namespace Godot
txt += ToHex32(b);
if (includeAlpha)
- txt = ToHex32(a) + txt;
+ {
+ txt += ToHex32(a);
+ }
return txt;
}
- // Constructors
+ /// <summary>
+ /// Constructs a color from RGBA values on the range of 0 to 1.
+ /// </summary>
+ /// <param name="r">The color's red component, typically on the range of 0 to 1.</param>
+ /// <param name="g">The color's green component, typically on the range of 0 to 1.</param>
+ /// <param name="b">The color's blue component, typically on the range of 0 to 1.</param>
+ /// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param>
public Color(float r, float g, float b, float a = 1.0f)
{
this.r = r;
@@ -429,6 +500,11 @@ namespace Godot
this.a = a;
}
+ /// <summary>
+ /// Constructs a color from an existing color and an alpha value.
+ /// </summary>
+ /// <param name="c">The color to construct from. Only its RGB values are used.</param>
+ /// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param>
public Color(Color c, float a = 1.0f)
{
r = c.r;
@@ -437,6 +513,11 @@ namespace Godot
this.a = a;
}
+ /// <summary>
+ /// Constructs a color from a 32-bit integer
+ /// (each byte represents a component of the RGBA profile).
+ /// </summary>
+ /// <param name="rgba">The uint representing the color.</param>
public Color(uint rgba)
{
a = (rgba & 0xFF) / 255.0f;
@@ -448,6 +529,11 @@ namespace Godot
r = (rgba & 0xFF) / 255.0f;
}
+ /// <summary>
+ /// Constructs a color from a 64-bit integer
+ /// (each word represents a component of the RGBA profile).
+ /// </summary>
+ /// <param name="rgba">The ulong representing the color.</param>
public Color(ulong rgba)
{
a = (rgba & 0xFFFF) / 65535.0f;
@@ -459,41 +545,250 @@ namespace Godot
r = (rgba & 0xFFFF) / 65535.0f;
}
- private static int ParseCol8(string str, int ofs)
+ /// <summary>
+ /// Constructs a color from the HTML hexadecimal color string in RGBA format.
+ /// </summary>
+ /// <param name="rgba">A string for the HTML hexadecimal representation of this color.</param>
+ public Color(string rgba)
{
- int ig = 0;
+ if (rgba.Length == 0)
+ {
+ r = 0f;
+ g = 0f;
+ b = 0f;
+ a = 1.0f;
+ return;
+ }
- for (int i = 0; i < 2; i++)
+ if (rgba[0] == '#')
+ {
+ rgba = rgba.Substring(1);
+ }
+
+ // If enabled, use 1 hex digit per channel instead of 2.
+ // Other sizes aren't in the HTML/CSS spec but we could add them if desired.
+ bool isShorthand = rgba.Length < 5;
+ bool alpha;
+
+ if (rgba.Length == 8)
+ {
+ alpha = true;
+ }
+ else if (rgba.Length == 6)
+ {
+ alpha = false;
+ }
+ else if (rgba.Length == 4)
+ {
+ alpha = true;
+ }
+ else if (rgba.Length == 3)
+ {
+ alpha = false;
+ }
+ else
{
- int c = str[i + ofs];
- int v;
+ throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
+ }
- if (c >= '0' && c <= '9')
+ a = 1.0f;
+ if (isShorthand)
+ {
+ r = ParseCol4(rgba, 0) / 15f;
+ g = ParseCol4(rgba, 1) / 15f;
+ b = ParseCol4(rgba, 2) / 15f;
+ if (alpha)
{
- v = c - '0';
+ a = ParseCol4(rgba, 3) / 15f;
}
- else if (c >= 'a' && c <= 'f')
+ }
+ else
+ {
+ r = ParseCol8(rgba, 0) / 255f;
+ g = ParseCol8(rgba, 2) / 255f;
+ b = ParseCol8(rgba, 4) / 255f;
+ if (alpha)
{
- v = c - 'a';
- v += 10;
+ a = ParseCol8(rgba, 6) / 255f;
}
- else if (c >= 'A' && c <= 'F')
+ }
+
+ if (r < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
+ }
+
+ if (g < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
+ }
+
+ if (b < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
+ }
+
+ if (a < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
+ }
+ }
+
+ /// <summary>
+ /// Returns a color constructed from integer red, green, blue, and alpha channels.
+ /// Each channel should have 8 bits of information ranging from 0 to 255.
+ /// </summary>
+ /// <param name="r8">The red component represented on the range of 0 to 255.</param>
+ /// <param name="g8">The green component represented on the range of 0 to 255.</param>
+ /// <param name="b8">The blue component represented on the range of 0 to 255.</param>
+ /// <param name="a8">The alpha (transparency) component represented on the range of 0 to 255.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color Color8(byte r8, byte g8, byte b8, byte a8 = 255)
+ {
+ return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f);
+ }
+
+ /// <summary>
+ /// Returns a color according to the standardized name, with the
+ /// specified alpha value. Supported color names are the same as
+ /// the constants defined in <see cref="Colors"/>.
+ /// </summary>
+ /// <param name="name">The name of the color.</param>
+ /// <param name="alpha">The alpha (transparency) component represented on the range of 0 to 1. Default: 1.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color ColorN(string name, float alpha = 1f)
+ {
+ name = name.Replace(" ", String.Empty);
+ name = name.Replace("-", String.Empty);
+ name = name.Replace("_", String.Empty);
+ name = name.Replace("'", String.Empty);
+ name = name.Replace(".", String.Empty);
+ name = name.ToLower();
+
+ if (!Colors.namedColors.ContainsKey(name))
+ {
+ throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
+ }
+
+ Color color = Colors.namedColors[name];
+ color.a = alpha;
+ return color;
+ }
+
+ /// <summary>
+ /// Constructs a color from an HSV profile, with values on the
+ /// range of 0 to 1. This is equivalent to using each of
+ /// the `h`/`s`/`v` properties, but much more efficient.
+ /// </summary>
+ /// <param name="hue">The HSV hue, typically on the range of 0 to 1.</param>
+ /// <param name="saturation">The HSV saturation, typically on the range of 0 to 1.</param>
+ /// <param name="value">The HSV value (brightness), typically on the range of 0 to 1.</param>
+ /// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f)
+ {
+ if (saturation == 0)
+ {
+ // acp_hromatic (grey)
+ return new Color(value, value, value, alpha);
+ }
+
+ int i;
+ float f, p, q, t;
+
+ hue *= 6.0f;
+ hue %= 6f;
+ i = (int)hue;
+
+ f = hue - i;
+ p = value * (1 - saturation);
+ q = value * (1 - saturation * f);
+ t = value * (1 - saturation * (1 - f));
+
+ switch (i)
+ {
+ case 0: // Red is the dominant color
+ return new Color(value, t, p, alpha);
+ case 1: // Green is the dominant color
+ return new Color(q, value, p, alpha);
+ case 2:
+ return new Color(p, value, t, alpha);
+ case 3: // Blue is the dominant color
+ return new Color(p, q, value, alpha);
+ case 4:
+ return new Color(t, p, value, alpha);
+ default: // (5) Red is the dominant color
+ return new Color(value, p, q, alpha);
+ }
+ }
+
+ /// <summary>
+ /// Converts a color to HSV values. This is equivalent to using each of
+ /// the `h`/`s`/`v` properties, but much more efficient.
+ /// </summary>
+ /// <param name="hue">Output parameter for the HSV hue.</param>
+ /// <param name="saturation">Output parameter for the HSV saturation.</param>
+ /// <param name="value">Output parameter for the HSV value.</param>
+ public void ToHsv(out float hue, out float saturation, out float value)
+ {
+ float max = (float)Mathf.Max(r, Mathf.Max(g, b));
+ float min = (float)Mathf.Min(r, Mathf.Min(g, b));
+
+ float delta = max - min;
+
+ if (delta == 0)
+ {
+ hue = 0;
+ }
+ else
+ {
+ if (r == max)
{
- v = c - 'A';
- v += 10;
+ hue = (g - b) / delta; // Between yellow & magenta
+ }
+ else if (g == max)
+ {
+ hue = 2 + (b - r) / delta; // Between cyan & yellow
}
else
{
- return -1;
+ hue = 4 + (r - g) / delta; // Between magenta & cyan
}
- if (i == 0)
- ig += v * 16;
- else
- ig += v;
+ hue /= 6.0f;
+
+ if (hue < 0)
+ {
+ hue += 1.0f;
+ }
}
- return ig;
+ saturation = max == 0 ? 0 : 1f - 1f * min / max;
+ value = max;
+ }
+
+ private static int ParseCol4(string str, int ofs)
+ {
+ char character = str[ofs];
+
+ if (character >= '0' && character <= '9')
+ {
+ return character - '0';
+ }
+ else if (character >= 'a' && character <= 'f')
+ {
+ return character + (10 - 'a');
+ }
+ else if (character >= 'A' && character <= 'F')
+ {
+ return character + (10 - 'A');
+ }
+ return -1;
+ }
+
+ private static int ParseCol8(string str, int ofs)
+ {
+ return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1);
}
private String ToHex32(float val)
@@ -508,9 +803,13 @@ namespace Godot
int lv = v & 0xF;
if (lv < 10)
+ {
c = (char)('0' + lv);
+ }
else
+ {
c = (char)('a' + lv - 10);
+ }
v >>= 4;
ret = c + ret;
@@ -522,105 +821,31 @@ namespace Godot
internal static bool HtmlIsValid(string color)
{
if (color.Length == 0)
+ {
return false;
+ }
if (color[0] == '#')
- color = color.Substring(1, color.Length - 1);
-
- bool alpha;
-
- switch (color.Length)
{
- case 8:
- alpha = true;
- break;
- case 6:
- alpha = false;
- break;
- default:
- return false;
+ color = color.Substring(1);
}
- if (alpha)
+ // Check if the amount of hex digits is valid.
+ int len = color.Length;
+ if (!(len == 3 || len == 4 || len == 6 || len == 8))
{
- if (ParseCol8(color, 0) < 0)
- return false;
- }
-
- int from = alpha ? 2 : 0;
-
- if (ParseCol8(color, from + 0) < 0)
- return false;
- if (ParseCol8(color, from + 2) < 0)
return false;
- if (ParseCol8(color, from + 4) < 0)
- return false;
-
- return true;
- }
-
- public static Color Color8(byte r8, byte g8, byte b8, byte a8 = 255)
- {
- return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f);
- }
-
- public Color(string rgba)
- {
- if (rgba.Length == 0)
- {
- r = 0f;
- g = 0f;
- b = 0f;
- a = 1.0f;
- return;
}
- if (rgba[0] == '#')
- rgba = rgba.Substring(1);
-
- bool alpha;
-
- if (rgba.Length == 8)
- {
- alpha = true;
- }
- else if (rgba.Length == 6)
- {
- alpha = false;
- }
- else
- {
- throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
- }
-
- if (alpha)
- {
- a = ParseCol8(rgba, 0) / 255f;
-
- if (a < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
- }
- else
- {
- a = 1.0f;
+ // Check if each hex digit is valid.
+ for (int i = 0; i < len; i++) {
+ if (ParseCol4(color, i) == -1)
+ {
+ return false;
+ }
}
- int from = alpha ? 2 : 0;
-
- r = ParseCol8(rgba, from + 0) / 255f;
-
- if (r < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
-
- g = ParseCol8(rgba, from + 2) / 255f;
-
- if (g < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
-
- b = ParseCol8(rgba, from + 4) / 255f;
-
- if (b < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
+ return true;
}
public static Color operator +(Color left, Color right)
@@ -708,13 +933,13 @@ namespace Godot
if (Mathf.IsEqualApprox(left.g, right.g))
{
if (Mathf.IsEqualApprox(left.b, right.b))
+ {
return left.a < right.a;
+ }
return left.b < right.b;
}
-
return left.g < right.g;
}
-
return left.r < right.r;
}
@@ -725,13 +950,13 @@ namespace Godot
if (Mathf.IsEqualApprox(left.g, right.g))
{
if (Mathf.IsEqualApprox(left.b, right.b))
+ {
return left.a > right.a;
+ }
return left.b > right.b;
}
-
return left.g > right.g;
}
-
return left.r > right.r;
}
@@ -750,6 +975,12 @@ namespace Godot
return r == other.r && g == other.g && b == other.b && a == other.a;
}
+ /// <summary>
+ /// Returns true if this color and `other` are approximately equal, by running
+ /// <see cref="Godot.Mathf.IsEqualApprox(float, float)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other color to compare.</param>
+ /// <returns>Whether or not the colors are approximately equal.</returns>
public bool IsEqualApprox(Color other)
{
return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index f41f5e9fc8..d05a0414aa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -3,6 +3,10 @@ using System.Collections.Generic;
namespace Godot
{
+ /// <summary>
+ /// This class contains color constants created from standardized color names.
+ /// The standardized color set is based on the X11 and .NET color names.
+ /// </summary>
public static class Colors
{
// Color names and values are derived from core/color_names.inc
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
new file mode 100644
index 0000000000..20b11a48dd
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.Collections;
+
+namespace Godot
+{
+ public partial class SceneTree
+ {
+ public Array<T> GetNodesInGroup<T>(StringName group) where T : class
+ {
+ return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(Object.GetPtr(this), StringName.GetPtr(group), typeof(T)));
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static 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 9384da0e48..e050d1fdd1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -84,7 +84,7 @@ namespace Godot
public static void Print(params object[] what)
{
- godot_icall_GD_print(Array.ConvertAll(what, x => x?.ToString()));
+ godot_icall_GD_print(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
}
public static void PrintStack()
@@ -94,22 +94,22 @@ namespace Godot
public static void PrintErr(params object[] what)
{
- godot_icall_GD_printerr(Array.ConvertAll(what, x => x?.ToString()));
+ godot_icall_GD_printerr(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
}
public static void PrintRaw(params object[] what)
{
- godot_icall_GD_printraw(Array.ConvertAll(what, x => x?.ToString()));
+ godot_icall_GD_printraw(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
}
public static void PrintS(params object[] what)
{
- godot_icall_GD_prints(Array.ConvertAll(what, x => x?.ToString()));
+ godot_icall_GD_prints(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
}
public static void PrintT(params object[] what)
{
- godot_icall_GD_printt(Array.ConvertAll(what, x => x?.ToString()));
+ godot_icall_GD_printt(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
}
public static float Randf()
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 4f7aa99df8..6eecc262d6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -11,79 +11,185 @@ namespace Godot
{
// Define constants with Decimal precision and cast down to double or float.
+ /// <summary>
+ /// The circle constant, the circumference of the unit circle in radians.
+ /// </summary>
public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959
+
+ /// <summary>
+ /// Constant that represents how many times the diameter of a circle
+ /// fits around its perimeter. This is equivalent to `Mathf.Tau / 2`.
+ /// </summary>
public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+
+ /// <summary>
+ /// Positive infinity. For negative infinity, use `-Mathf.Inf`.
+ /// </summary>
public const real_t Inf = real_t.PositiveInfinity;
+
+ /// <summary>
+ /// "Not a Number", an invalid value. `NaN` has special properties, including
+ /// that it is not equal to itself. It is output by some invalid operations,
+ /// such as dividing zero by zero.
+ /// </summary>
public const real_t NaN = real_t.NaN;
private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433
private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823
+ /// <summary>
+ /// Returns the absolute value of `s` (i.e. positive value).
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>The absolute value of `s`.</returns>
public static int Abs(int s)
{
return Math.Abs(s);
}
+ /// <summary>
+ /// Returns the absolute value of `s` (i.e. positive value).
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>The absolute value of `s`.</returns>
public static real_t Abs(real_t s)
{
return Math.Abs(s);
}
+ /// <summary>
+ /// Returns the arc cosine of `s` in radians. Use to get the angle of cosine s.
+ /// </summary>
+ /// <param name="s">The input cosine value. Must be on the range of -1.0 to 1.0.</param>
+ /// <returns>An angle that would result in the given cosine value. On the range `0` to `Tau/2`.</returns>
public static real_t Acos(real_t s)
{
return (real_t)Math.Acos(s);
}
+ /// <summary>
+ /// Returns the arc sine of `s` in radians. Use to get the angle of sine s.
+ /// </summary>
+ /// <param name="s">The input sine value. Must be on the range of -1.0 to 1.0.</param>
+ /// <returns>An angle that would result in the given sine value. On the range `-Tau/4` to `Tau/4`.</returns>
public static real_t Asin(real_t s)
{
return (real_t)Math.Asin(s);
}
+ /// <summary>
+ /// Returns the arc tangent of `s` in radians. Use to get the angle of tangent s.
+ ///
+ /// The method cannot know in which quadrant the angle should fall.
+ /// See <see cref="Atan2(real_t, real_t)"/> if you have both `y` and `x`.
+ /// </summary>
+ /// <param name="s">The input tangent value.</param>
+ /// <returns>An angle that would result in the given tangent value. On the range `-Tau/4` to `Tau/4`.</returns>
public static real_t Atan(real_t s)
{
return (real_t)Math.Atan(s);
}
+ /// <summary>
+ /// Returns the arc tangent of `y` and `x` in radians. Use to get the angle
+ /// of the tangent of `y/x`. To compute the value, the method takes into
+ /// account the sign of both arguments in order to determine the quadrant.
+ ///
+ /// Important note: The Y coordinate comes first, by convention.
+ /// </summary>
+ /// <param name="y">The Y coordinate of the point to find the angle to.</param>
+ /// <param name="x">The X coordinate of the point to find the angle to.</param>
+ /// <returns>An angle that would result in the given tangent value. On the range `-Tau/2` to `Tau/2`.</returns>
public static real_t Atan2(real_t y, real_t x)
{
return (real_t)Math.Atan2(y, x);
}
+ /// <summary>
+ /// Converts a 2D point expressed in the cartesian coordinate
+ /// system (X and Y axis) to the polar coordinate system
+ /// (a distance from the origin and an angle).
+ /// </summary>
+ /// <param name="x">The input X coordinate.</param>
+ /// <param name="y">The input Y coordinate.</param>
+ /// <returns>A <see cref="Vector2"/> with X representing the distance and Y representing the angle.</returns>
public static Vector2 Cartesian2Polar(real_t x, real_t y)
{
return new Vector2(Sqrt(x * x + y * y), Atan2(y, x));
}
+ /// <summary>
+ /// Rounds `s` upward (towards positive infinity).
+ /// </summary>
+ /// <param name="s">The number to ceil.</param>
+ /// <returns>The smallest whole number that is not less than `s`.</returns>
public static real_t Ceil(real_t s)
{
return (real_t)Math.Ceiling(s);
}
+ /// <summary>
+ /// Clamps a `value` so that it is not less than `min` and not more than `max`.
+ /// </summary>
+ /// <param name="value">The value to clamp.</param>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The clamped value.</returns>
public static int Clamp(int value, int min, int max)
{
return value < min ? min : value > max ? max : value;
}
+ /// <summary>
+ /// Clamps a `value` so that it is not less than `min` and not more than `max`.
+ /// </summary>
+ /// <param name="value">The value to clamp.</param>
+ /// <param name="min">The minimum allowed value.</param>
+ /// <param name="max">The maximum allowed value.</param>
+ /// <returns>The clamped value.</returns>
public static real_t Clamp(real_t value, real_t min, real_t max)
{
return value < min ? min : value > max ? max : value;
}
+ /// <summary>
+ /// Returns the cosine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The cosine of that angle.</returns>
public static real_t Cos(real_t s)
{
return (real_t)Math.Cos(s);
}
+ /// <summary>
+ /// Returns the hyperbolic cosine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic cosine of that angle.</returns>
public static real_t Cosh(real_t s)
{
return (real_t)Math.Cosh(s);
}
+ /// <summary>
+ /// Converts an angle expressed in degrees to radians.
+ /// </summary>
+ /// <param name="deg">An angle expressed in degrees.</param>
+ /// <returns>The same angle expressed in radians.</returns>
public static real_t Deg2Rad(real_t deg)
{
return deg * Deg2RadConst;
}
+ /// <summary>
+ /// Easing function, based on exponent. The curve values are:
+ /// `0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out.
+ /// Negative values are in-out/out-in.
+ /// </summary>
+ /// <param name="s">The value to ease.</param>
+ /// <param name="curve">`0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out.</param>
+ /// <returns>The eased value.</returns>
public static real_t Ease(real_t s, real_t curve)
{
if (s < 0f)
@@ -118,21 +224,47 @@ namespace Godot
return 0f;
}
+ /// <summary>
+ /// The natural exponential function. It raises the mathematical
+ /// constant `e` to the power of `s` and returns it.
+ /// </summary>
+ /// <param name="s">The exponent to raise `e` to.</param>
+ /// <returns>`e` raised to the power of `s`.</returns>
public static real_t Exp(real_t s)
{
return (real_t)Math.Exp(s);
}
+ /// <summary>
+ /// Rounds `s` downward (towards negative infinity).
+ /// </summary>
+ /// <param name="s">The number to floor.</param>
+ /// <returns>The largest whole number that is not more than `s`.</returns>
public static real_t Floor(real_t s)
{
return (real_t)Math.Floor(s);
}
+ /// <summary>
+ /// Returns a normalized value considering the given range.
+ /// This is the opposite of <see cref="Lerp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="from">The interpolated value.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the inverse interpolation.</returns>
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
return (weight - from) / (to - from);
}
+ /// <summary>
+ /// Returns true if `a` and `b` are approximately equal to each other.
+ /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>A bool for whether or not the two values are approximately equal.</returns>
public static bool IsEqualApprox(real_t a, real_t b)
{
// Check for exact equality first, required to handle "infinity" values.
@@ -149,26 +281,62 @@ namespace Godot
return Abs(a - b) < tolerance;
}
+ /// <summary>
+ /// Returns whether `s` is an infinity value (either positive infinity or negative infinity).
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A bool for whether or not the value is an infinity value.</returns>
public static bool IsInf(real_t s)
{
return real_t.IsInfinity(s);
}
+ /// <summary>
+ /// Returns whether `s` is a `NaN` ("Not a Number" or invalid) value.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A bool for whether or not the value is a `NaN` value.</returns>
public static bool IsNaN(real_t s)
{
return real_t.IsNaN(s);
}
+ /// <summary>
+ /// Returns true if `s` is approximately zero.
+ /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
+ ///
+ /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A bool for whether or not the value is nearly zero.</returns>
public static bool IsZeroApprox(real_t s)
{
return Abs(s) < Epsilon;
}
+ /// <summary>
+ /// Linearly interpolates between two values by a normalized value.
+ /// This is the opposite <see cref="InverseLerp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
return from + (to - from) * weight;
}
+ /// <summary>
+ /// Linearly interpolates between two angles (in radians) by a normalized value.
+ ///
+ /// Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
+ /// but interpolates correctly when the angles wrap around <see cref="Tau"/>.
+ /// </summary>
+ /// <param name="from">The start angle for interpolation.</param>
+ /// <param name="to">The destination angle for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting angle of the interpolation.</returns>
public static real_t LerpAngle(real_t from, real_t to, real_t weight)
{
real_t difference = (to - from) % Mathf.Tau;
@@ -176,36 +344,81 @@ namespace Godot
return from + distance * weight;
}
+ /// <summary>
+ /// Natural logarithm. The amount of time needed to reach a certain level of continuous growth.
+ ///
+ /// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
+ /// </summary>
+ /// <param name="s">The input value.</param>
+ /// <returns>The natural log of `s`.</returns>
public static real_t Log(real_t s)
{
return (real_t)Math.Log(s);
}
+ /// <summary>
+ /// Returns the maximum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is higher.</returns>
public static int Max(int a, int b)
{
return a > b ? a : b;
}
+ /// <summary>
+ /// Returns the maximum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is higher.</returns>
public static real_t Max(real_t a, real_t b)
{
return a > b ? a : b;
}
+ /// <summary>
+ /// Returns the minimum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is lower.</returns>
public static int Min(int a, int b)
{
return a < b ? a : b;
}
+ /// <summary>
+ /// Returns the minimum of two values.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <returns>Whichever of the two values is lower.</returns>
public static real_t Min(real_t a, real_t b)
{
return a < b ? a : b;
}
+ /// <summary>
+ /// Moves `from` toward `to` by the `delta` value.
+ ///
+ /// Use a negative delta value to move away.
+ /// </summary>
+ /// <param name="from">The start value.</param>
+ /// <param name="to">The value to move towards.</param>
+ /// <param name="delta">The amount to move by.</param>
+ /// <returns>The value after moving.</returns>
public static real_t MoveToward(real_t from, real_t to, real_t delta)
{
return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta;
}
+ /// <summary>
+ /// Returns the nearest larger power of 2 for the integer `value`.
+ /// </summary>
+ /// <param name="value">The input value.</param>
+ /// <returns>The nearest larger power of 2.</returns>
public static int NearestPo2(int value)
{
value--;
@@ -218,14 +431,25 @@ namespace Godot
return value;
}
+ /// <summary>
+ /// Converts a 2D point expressed in the polar coordinate
+ /// system (a distance from the origin `r` and an angle `th`)
+ /// to the cartesian coordinate system (X and Y axis).
+ /// </summary>
+ /// <param name="r">The distance from the origin.</param>
+ /// <param name="th">The angle of the point.</param>
+ /// <returns>A <see cref="Vector2"/> representing the cartesian coordinate.</returns>
public static Vector2 Polar2Cartesian(real_t r, real_t th)
{
return new Vector2(r * Cos(th), r * Sin(th));
}
/// <summary>
- /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`.
/// </summary>
+ /// <param name="a">The dividend, the primary input.</param>
+ /// <param name="b">The divisor. The output is on the range `[0, b)`.</param>
+ /// <returns>The resulting output.</returns>
public static int PosMod(int a, int b)
{
int c = a % b;
@@ -237,8 +461,11 @@ namespace Godot
}
/// <summary>
- /// Performs a canonical Modulus operation, where the output is on the range [0, b).
+ /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`.
/// </summary>
+ /// <param name="a">The dividend, the primary input.</param>
+ /// <param name="b">The divisor. The output is on the range `[0, b)`.</param>
+ /// <returns>The resulting output.</returns>
public static real_t PosMod(real_t a, real_t b)
{
real_t c = a % b;
@@ -249,43 +476,89 @@ namespace Godot
return c;
}
+ /// <summary>
+ /// Returns the result of `x` raised to the power of `y`.
+ /// </summary>
+ /// <param name="x">The base.</param>
+ /// <param name="y">The exponent.</param>
+ /// <returns>`x` raised to the power of `y`.</returns>
public static real_t Pow(real_t x, real_t y)
{
return (real_t)Math.Pow(x, y);
}
+ /// <summary>
+ /// Converts an angle expressed in radians to degrees.
+ /// </summary>
+ /// <param name="rad">An angle expressed in radians.</param>
+ /// <returns>The same angle expressed in degrees.</returns>
public static real_t Rad2Deg(real_t rad)
{
return rad * Rad2DegConst;
}
+ /// <summary>
+ /// Rounds `s` to the nearest whole number,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <param name="s">The number to round.</param>
+ /// <returns>The rounded number.</returns>
public static real_t Round(real_t s)
{
return (real_t)Math.Round(s);
}
+ /// <summary>
+ /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`.
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>One of three possible values: `1`, `-1`, or `0`.</returns>
public static int Sign(int s)
{
if (s == 0) return 0;
return s < 0 ? -1 : 1;
}
+ /// <summary>
+ /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`.
+ /// </summary>
+ /// <param name="s">The input number.</param>
+ /// <returns>One of three possible values: `1`, `-1`, or `0`.</returns>
public static int Sign(real_t s)
{
if (s == 0) return 0;
return s < 0 ? -1 : 1;
}
+ /// <summary>
+ /// Returns the sine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The sine of that angle.</returns>
public static real_t Sin(real_t s)
{
return (real_t)Math.Sin(s);
}
+ /// <summary>
+ /// Returns the hyperbolic sine of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic sine of that angle.</returns>
public static real_t Sinh(real_t s)
{
return (real_t)Math.Sinh(s);
}
+ /// <summary>
+ /// Returns a number smoothly interpolated between `from` and `to`,
+ /// based on the `weight`. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
+ /// but interpolates faster at the beginning and slower at the end.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="weight">A value representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
public static real_t SmoothStep(real_t from, real_t to, real_t weight)
{
if (IsEqualApprox(from, to))
@@ -296,11 +569,25 @@ namespace Godot
return x * x * (3 - 2 * x);
}
+ /// <summary>
+ /// Returns the square root of `s`, where `s` is a non-negative number.
+ ///
+ /// If you need negative inputs, use `System.Numerics.Complex`.
+ /// </summary>
+ /// <param name="s">The input number. Must not be negative.</param>
+ /// <returns>The square root of `s`.</returns>
public static real_t Sqrt(real_t s)
{
return (real_t)Math.Sqrt(s);
}
+ /// <summary>
+ /// Returns the position of the first non-zero digit, after the
+ /// decimal point. Note that the maximum return value is 10,
+ /// which is a design decision in the implementation.
+ /// </summary>
+ /// <param name="step">The input value.</param>
+ /// <returns>The position of the first non-zero digit.</returns>
public static int StepDecimals(real_t step)
{
double[] sd = new double[] {
@@ -326,32 +613,68 @@ namespace Godot
return 0;
}
+ /// <summary>
+ /// Snaps float value `s` to a given `step`.
+ /// This can also be used to round a floating point
+ /// number to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="s">The value to stepify.</param>
+ /// <param name="step">The step size to snap to.</param>
+ /// <returns></returns>
public static real_t Stepify(real_t s, real_t step)
{
if (step != 0f)
{
- s = Floor(s / step + 0.5f) * step;
+ return Floor(s / step + 0.5f) * step;
}
return s;
}
+ /// <summary>
+ /// Returns the tangent of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The tangent of that angle.</returns>
public static real_t Tan(real_t s)
{
return (real_t)Math.Tan(s);
}
+ /// <summary>
+ /// Returns the hyperbolic tangent of angle `s` in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The hyperbolic tangent of that angle.</returns>
public static real_t Tanh(real_t s)
{
return (real_t)Math.Tanh(s);
}
+ /// <summary>
+ /// Wraps `value` between `min` and `max`. Usable for creating loop-alike
+ /// behavior or infinite surfaces. If `min` is `0`, this is equivalent
+ /// to <see cref="PosMod(int, int)"/>, so prefer using that instead.
+ /// </summary>
+ /// <param name="value">The value to wrap.</param>
+ /// <param name="min">The minimum allowed value and lower bound of the range.</param>
+ /// <param name="max">The maximum allowed value and upper bound of the range.</param>
+ /// <returns>The wrapped value.</returns>
public static int Wrap(int value, int min, int max)
{
int range = max - min;
return range == 0 ? min : min + ((value - min) % range + range) % range;
}
+ /// <summary>
+ /// Wraps `value` between `min` and `max`. Usable for creating loop-alike
+ /// behavior or infinite surfaces. If `min` is `0`, this is equivalent
+ /// to <see cref="PosMod(real_t, real_t)"/>, so prefer using that instead.
+ /// </summary>
+ /// <param name="value">The value to wrap.</param>
+ /// <param name="min">The minimum allowed value and lower bound of the range.</param>
+ /// <param name="max">The maximum allowed value and upper bound of the range.</param>
+ /// <returns>The wrapped value.</returns>
public static real_t Wrap(real_t value, real_t min, real_t max)
{
real_t range = max - min;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index 1b7fd4906f..c2f4701b5f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -12,40 +12,89 @@ namespace Godot
{
// Define constants with Decimal precision and cast down to double or float.
+ /// <summary>
+ /// The natural number `e`.
+ /// </summary>
public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045
+
+ /// <summary>
+ /// The square root of 2.
+ /// </summary>
public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
+ /// <summary>
+ /// A very small number used for float comparison with error tolerance.
+ /// 1e-06 with single-precision floats, but 1e-14 if `REAL_T_IS_DOUBLE`.
+ /// </summary>
#if REAL_T_IS_DOUBLE
public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
#else
public const real_t Epsilon = 1e-06f;
#endif
+ /// <summary>
+ /// Returns the amount of digits after the decimal place.
+ /// </summary>
+ /// <param name="s">The input value.</param>
+ /// <returns>The amount of digits.</returns>
public static int DecimalCount(real_t s)
{
return DecimalCount((decimal)s);
}
+ /// <summary>
+ /// Returns the amount of digits after the decimal place.
+ /// </summary>
+ /// <param name="s">The input <see cref="System.Decimal"/> value.</param>
+ /// <returns>The amount of digits.</returns>
public static int DecimalCount(decimal s)
{
return BitConverter.GetBytes(decimal.GetBits(s)[3])[2];
}
+ /// <summary>
+ /// Rounds `s` upward (towards positive infinity).
+ ///
+ /// This is the same as <see cref="Ceil(real_t)"/>, but returns an `int`.
+ /// </summary>
+ /// <param name="s">The number to ceil.</param>
+ /// <returns>The smallest whole number that is not less than `s`.</returns>
public static int CeilToInt(real_t s)
{
return (int)Math.Ceiling(s);
}
+ /// <summary>
+ /// Rounds `s` downward (towards negative infinity).
+ ///
+ /// This is the same as <see cref="Floor(real_t)"/>, but returns an `int`.
+ /// </summary>
+ /// <param name="s">The number to floor.</param>
+ /// <returns>The largest whole number that is not more than `s`.</returns>
public static int FloorToInt(real_t s)
{
return (int)Math.Floor(s);
}
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="s"></param>
+ /// <returns></returns>
public static int RoundToInt(real_t s)
{
return (int)Math.Round(s);
}
+ /// <summary>
+ /// Returns true if `a` and `b` are approximately equal to each other.
+ /// The comparison is done using the provided tolerance value.
+ /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="a">One of the values.</param>
+ /// <param name="b">The other value.</param>
+ /// <param name="tolerance">The pre-calculated tolerance value.</param>
+ /// <returns>A bool for whether or not the two values are equal.</returns>
public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance)
{
// Check for exact equality first, required to handle "infinity" values.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 885845e3a4..2f8b5f297c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -8,18 +8,33 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// Plane represents a normalized plane equation.
+ /// "Over" or "Above" the plane is considered the side of
+ /// the plane towards where the normal is pointing.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Plane : IEquatable<Plane>
{
private Vector3 _normal;
+ /// <summary>
+ /// The normal of the plane, which must be normalized.
+ /// In the scalar equation of the plane `ax + by + cz = d`, this is
+ /// the vector `(a, b, c)`, where `d` is the <see cref="D"/> property.
+ /// </summary>
+ /// <value>Equivalent to `x`, `y`, and `z`.</value>
public Vector3 Normal
{
get { return _normal; }
set { _normal = value; }
}
+ /// <summary>
+ /// The X component of the plane's normal vector.
+ /// </summary>
+ /// <value>Equivalent to <see cref="Normal"/>'s X value.</value>
public real_t x
{
get
@@ -32,6 +47,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The Y component of the plane's normal vector.
+ /// </summary>
+ /// <value>Equivalent to <see cref="Normal"/>'s Y value.</value>
public real_t y
{
get
@@ -44,6 +63,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The Z component of the plane's normal vector.
+ /// </summary>
+ /// <value>Equivalent to <see cref="Normal"/>'s Z value.</value>
public real_t z
{
get
@@ -56,38 +79,71 @@ namespace Godot
}
}
+ /// <summary>
+ /// The distance from the origin to the plane (in the direction of
+ /// <see cref="Normal"/>). This value is typically non-negative.
+ /// In the scalar equation of the plane `ax + by + cz = d`,
+ /// this is `d`, while the `(a, b, c)` coordinates are represented
+ /// by the <see cref="Normal"/> property.
+ /// </summary>
+ /// <value>The plane's distance from the origin.</value>
public real_t D { get; set; }
+ /// <summary>
+ /// The center of the plane, the point where the normal line intersects the plane.
+ /// </summary>
+ /// <value>Equivalent to <see cref="Normal"/> multiplied by `D`.</value>
public Vector3 Center
{
get
{
return _normal * D;
}
+ set
+ {
+ _normal = value.Normalized();
+ D = value.Length();
+ }
}
+ /// <summary>
+ /// Returns the shortest distance from this plane to the position `point`.
+ /// </summary>
+ /// <param name="point">The position to use for the calculation.</param>
+ /// <returns>The shortest distance.</returns>
public real_t DistanceTo(Vector3 point)
{
return _normal.Dot(point) - D;
}
- public Vector3 GetAnyPoint()
- {
- return _normal * D;
- }
-
+ /// <summary>
+ /// Returns true if point is inside the plane.
+ /// Comparison uses a custom minimum epsilon threshold.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <param name="epsilon">The tolerance threshold.</param>
+ /// <returns>A bool for whether or not the plane has the point.</returns>
public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon)
{
real_t dist = _normal.Dot(point) - D;
return Mathf.Abs(dist) <= epsilon;
}
+ /// <summary>
+ /// Returns the intersection point of the three planes: `b`, `c`,
+ /// and this plane. If no intersection is found, `null` is returned.
+ /// </summary>
+ /// <param name="b">One of the three planes to use in the calculation.</param>
+ /// <param name="c">One of the three planes to use in the calculation.</param>
+ /// <returns>The intersection, or `null` if none is found.</returns>
public Vector3? Intersect3(Plane b, Plane c)
{
real_t denom = _normal.Cross(b._normal).Dot(c._normal);
if (Mathf.IsZeroApprox(denom))
+ {
return null;
+ }
Vector3 result = b._normal.Cross(c._normal) * D +
c._normal.Cross(_normal) * b.D +
@@ -96,54 +152,94 @@ namespace Godot
return result / denom;
}
+ /// <summary>
+ /// Returns the intersection point of a ray consisting of the
+ /// position `from` and the direction normal `dir` with this plane.
+ /// If no intersection is found, `null` is returned.
+ /// </summary>
+ /// <param name="from">The start of the ray.</param>
+ /// <param name="dir">The direction of the ray, normalized.</param>
+ /// <returns>The intersection, or `null` if none is found.</returns>
public Vector3? IntersectRay(Vector3 from, Vector3 dir)
{
real_t den = _normal.Dot(dir);
if (Mathf.IsZeroApprox(den))
+ {
return null;
+ }
real_t dist = (_normal.Dot(from) - D) / den;
// This is a ray, before the emitting pos (from) does not exist
if (dist > Mathf.Epsilon)
+ {
return null;
+ }
return from + dir * -dist;
}
+ /// <summary>
+ /// Returns the intersection point of a line segment from
+ /// position `begin` to position `end` with this plane.
+ /// If no intersection is found, `null` is returned.
+ /// </summary>
+ /// <param name="begin">The start of the line segment.</param>
+ /// <param name="end">The end of the line segment.</param>
+ /// <returns>The intersection, or `null` if none is found.</returns>
public Vector3? IntersectSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
real_t den = _normal.Dot(segment);
if (Mathf.IsZeroApprox(den))
+ {
return null;
+ }
real_t dist = (_normal.Dot(begin) - D) / den;
// Only allow dist to be in the range of 0 to 1, with tolerance.
if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon)
+ {
return null;
+ }
return begin + segment * -dist;
}
+ /// <summary>
+ /// Returns true if `point` is located above the plane.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the point is above the plane.</returns>
public bool IsPointOver(Vector3 point)
{
return _normal.Dot(point) > D;
}
+ /// <summary>
+ /// Returns the plane scaled to unit length.
+ /// </summary>
+ /// <returns>A normalized version of the plane.</returns>
public Plane Normalized()
{
real_t len = _normal.Length();
if (len == 0)
+ {
return new Plane(0, 0, 0, 0);
+ }
return new Plane(_normal / len, D / len);
}
+ /// <summary>
+ /// Returns the orthogonal projection of `point` into the plane.
+ /// </summary>
+ /// <param name="point">The point to project.</param>
+ /// <returns>The projected point.</returns>
public Vector3 Project(Vector3 point)
{
return point - _normal * DistanceTo(point);
@@ -154,22 +250,56 @@ namespace Godot
private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0);
private static readonly Plane _planeXY = new Plane(0, 0, 1, 0);
+ /// <summary>
+ /// A plane that extends in the Y and Z axes (normal vector points +X).
+ /// </summary>
+ /// <value>Equivalent to `new Plane(1, 0, 0, 0)`.</value>
public static Plane PlaneYZ { get { return _planeYZ; } }
+
+ /// <summary>
+ /// A plane that extends in the X and Z axes (normal vector points +Y).
+ /// </summary>
+ /// <value>Equivalent to `new Plane(0, 1, 0, 0)`.</value>
public static Plane PlaneXZ { get { return _planeXZ; } }
+
+ /// <summary>
+ /// A plane that extends in the X and Y axes (normal vector points +Z).
+ /// </summary>
+ /// <value>Equivalent to `new Plane(0, 0, 1, 0)`.</value>
public static Plane PlaneXY { get { return _planeXY; } }
- // Constructors
+ /// <summary>
+ /// Constructs a plane from four values. `a`, `b` and `c` become the
+ /// components of the resulting plane's <see cref="Normal"/> vector.
+ /// `d` becomes the plane's distance from the origin.
+ /// </summary>
+ /// <param name="a">The X component of the plane's normal vector.</param>
+ /// <param name="b">The Y component of the plane's normal vector.</param>
+ /// <param name="c">The Z component of the plane's normal vector.</param>
+ /// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
public Plane(real_t a, real_t b, real_t c, real_t d)
{
_normal = new Vector3(a, b, c);
this.D = d;
}
+
+ /// <summary>
+ /// Constructs a plane from a normal vector and the plane's distance to the origin.
+ /// </summary>
+ /// <param name="normal">The normal of the plane, must be normalized.</param>
+ /// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
public Plane(Vector3 normal, real_t d)
{
this._normal = normal;
this.D = d;
}
+ /// <summary>
+ /// Constructs a plane from the three points, given in clockwise order.
+ /// </summary>
+ /// <param name="v1">The first point.</param>
+ /// <param name="v2">The second point.</param>
+ /// <param name="v3">The third point.</param>
public Plane(Vector3 v1, Vector3 v2, Vector3 v3)
{
_normal = (v1 - v3).Cross(v1 - v2);
@@ -207,6 +337,12 @@ namespace Godot
return _normal == other._normal && D == other.D;
}
+ /// <summary>
+ /// Returns true if this plane and `other` are approximately equal, by running
+ /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other plane to compare.</param>
+ /// <returns>Whether or not the planes are approximately equal.</returns>
public bool IsEqualApprox(Plane other)
{
return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
index bbc617ea6e..b33490f9cb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
@@ -8,15 +8,51 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// A unit quaternion used for representing 3D rotations.
+ /// Quaternions need to be normalized to be used for rotation.
+ ///
+ /// It is similar to Basis, which implements matrix representation of
+ /// rotations, and can be parametrized using both an axis-angle pair
+ /// or Euler angles. Basis stores rotation, scale, and shearing,
+ /// while Quat only stores rotation.
+ ///
+ /// Due to its compactness and the way it is stored in memory, certain
+ /// operations (obtaining axis-angle and performing SLERP, in particular)
+ /// are more efficient and robust against floating-point errors.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Quat : IEquatable<Quat>
{
+ /// <summary>
+ /// X component of the quaternion (imaginary `i` axis part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
public real_t x;
+
+ /// <summary>
+ /// Y component of the quaternion (imaginary `j` axis part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
public real_t y;
+
+ /// <summary>
+ /// Z component of the quaternion (imaginary `k` axis part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
public real_t z;
+
+ /// <summary>
+ /// W component of the quaternion (real part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
public real_t w;
+ /// <summary>
+ /// Access quaternion components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`, `[3]` is equivalent to `.w`.</value>
public real_t this[int index]
{
get
@@ -57,16 +93,35 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the length (magnitude) of the quaternion.
+ /// </summary>
+ /// <value>Equivalent to `Mathf.Sqrt(LengthSquared)`.</value>
public real_t Length
{
get { return Mathf.Sqrt(LengthSquared); }
}
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of the quaternion.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare quaternions or need the squared length for some formula.
+ /// </summary>
+ /// <value>Equivalent to `Dot(this)`.</value>
public real_t LengthSquared
{
get { return Dot(this); }
}
+ /// <summary>
+ /// Performs a cubic spherical interpolation between quaternions `preA`,
+ /// this vector, `b`, and `postB`, by the given amount `t`.
+ /// </summary>
+ /// <param name="b">The destination quaternion.</param>
+ /// <param name="preA">A quaternion before this quaternion.</param>
+ /// <param name="postB">A quaternion after `b`.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated quaternion.</returns>
public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t)
{
real_t t2 = (1.0f - t) * t * 2f;
@@ -75,85 +130,131 @@ namespace Godot
return sp.Slerpni(sq, t2);
}
+ /// <summary>
+ /// Returns the dot product of two quaternions.
+ /// </summary>
+ /// <param name="b">The other quaternion.</param>
+ /// <returns>The dot product.</returns>
public real_t Dot(Quat b)
{
return x * b.x + y * b.y + z * b.z + w * b.w;
}
+ /// <summary>
+ /// Returns Euler angles (in the YXZ convention: when decomposing,
+ /// first Z, then X, and Y last) corresponding to the rotation
+ /// represented by the unit quaternion. Returned vector contains
+ /// the rotation angles in the format (X angle, Y angle, Z angle).
+ /// </summary>
+ /// <returns>The Euler angle representation of this quaternion.</returns>
public Vector3 GetEuler()
{
#if DEBUG
if (!IsNormalized())
+ {
throw new InvalidOperationException("Quat is not normalized");
+ }
#endif
var basis = new Basis(this);
return basis.GetEuler();
}
+ /// <summary>
+ /// Returns the inverse of the quaternion.
+ /// </summary>
+ /// <returns>The inverse quaternion.</returns>
public Quat Inverse()
{
#if DEBUG
if (!IsNormalized())
+ {
throw new InvalidOperationException("Quat is not normalized");
+ }
#endif
return new Quat(-x, -y, -z, w);
}
+ /// <summary>
+ /// Returns whether the quaternion is normalized or not.
+ /// </summary>
+ /// <returns>A bool for whether the quaternion is normalized or not.</returns>
+ public bool IsNormalized()
+ {
+ return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
+ }
+
+ /// <summary>
+ /// Returns a copy of the quaternion, normalized to unit length.
+ /// </summary>
+ /// <returns>The normalized quaternion.</returns>
public Quat Normalized()
{
return this / Length;
}
- public Quat Slerp(Quat b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this quaternion and `to` by amount `weight`.
+ ///
+ /// Note: Both quaternions must be normalized.
+ /// </summary>
+ /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting quaternion of the interpolation.</returns>
+ public Quat Slerp(Quat to, real_t weight)
{
#if DEBUG
if (!IsNormalized())
+ {
throw new InvalidOperationException("Quat is not normalized");
- if (!b.IsNormalized())
- throw new ArgumentException("Argument is not normalized", nameof(b));
+ }
+ if (!to.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(to));
+ }
#endif
- // Calculate cosine
- real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w;
+ // Calculate cosine.
+ real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
var to1 = new Quat();
- // Adjust signs if necessary
+ // Adjust signs if necessary.
if (cosom < 0.0)
{
cosom = -cosom;
- to1.x = -b.x;
- to1.y = -b.y;
- to1.z = -b.z;
- to1.w = -b.w;
+ to1.x = -to.x;
+ to1.y = -to.y;
+ to1.z = -to.z;
+ to1.w = -to.w;
}
else
{
- to1.x = b.x;
- to1.y = b.y;
- to1.z = b.z;
- to1.w = b.w;
+ to1.x = to.x;
+ to1.y = to.y;
+ to1.z = to.z;
+ to1.w = to.w;
}
real_t sinom, scale0, scale1;
- // Calculate coefficients
+ // Calculate coefficients.
if (1.0 - cosom > Mathf.Epsilon)
{
- // Standard case (Slerp)
+ // Standard case (Slerp).
real_t omega = Mathf.Acos(cosom);
sinom = Mathf.Sin(omega);
- scale0 = Mathf.Sin((1.0f - t) * omega) / sinom;
- scale1 = Mathf.Sin(t * omega) / sinom;
+ scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom;
+ scale1 = Mathf.Sin(weight * omega) / sinom;
}
else
{
- // Quaternions are very close so we can do a linear interpolation
- scale0 = 1.0f - t;
- scale1 = t;
+ // Quaternions are very close so we can do a linear interpolation.
+ scale0 = 1.0f - weight;
+ scale1 = weight;
}
- // Calculate final values
+ // Calculate final values.
return new Quat
(
scale0 * x + scale1 * to1.x,
@@ -163,9 +264,17 @@ namespace Godot
);
}
- public Quat Slerpni(Quat b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this quaternion and `to` by amount `weight`, but without
+ /// checking if the rotation path is not bigger than 90 degrees.
+ /// </summary>
+ /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting quaternion of the interpolation.</returns>
+ public Quat Slerpni(Quat to, real_t weight)
{
- real_t dot = Dot(b);
+ real_t dot = Dot(to);
if (Mathf.Abs(dot) > 0.9999f)
{
@@ -174,33 +283,54 @@ namespace Godot
real_t theta = Mathf.Acos(dot);
real_t sinT = 1.0f / Mathf.Sin(theta);
- real_t newFactor = Mathf.Sin(t * theta) * sinT;
- real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT;
+ real_t newFactor = Mathf.Sin(weight * theta) * sinT;
+ real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
return new Quat
(
- invFactor * x + newFactor * b.x,
- invFactor * y + newFactor * b.y,
- invFactor * z + newFactor * b.z,
- invFactor * w + newFactor * b.w
+ invFactor * x + newFactor * to.x,
+ invFactor * y + newFactor * to.y,
+ invFactor * z + newFactor * to.z,
+ invFactor * w + newFactor * to.w
);
}
+ /// <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("Quat is not normalized");
+ }
#endif
var u = new Vector3(x, y, z);
Vector3 uv = u.Cross(v);
return v + ((uv * w) + u.Cross(uv)) * 2;
}
- // Static Readonly Properties
- public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f);
-
- // Constructors
+ // Constants
+ private static readonly Quat _identity = new Quat(0, 0, 0, 1);
+
+ /// <summary>
+ /// The identity quaternion, representing no rotation.
+ /// Equivalent to an identity <see cref="Basis"/> matrix. If a vector is transformed by
+ /// an identity quaternion, it will not change.
+ /// </summary>
+ /// <value>Equivalent to `new Quat(0, 0, 0, 1)`.</value>
+ public static Quat Identity { get { return _identity; } }
+
+ /// <summary>
+ /// Constructs a quaternion defined by the given values.
+ /// </summary>
+ /// <param name="x">X component of the quaternion (imaginary `i` axis part).</param>
+ /// <param name="y">Y component of the quaternion (imaginary `j` axis part).</param>
+ /// <param name="z">Z component of the quaternion (imaginary `k` axis part).</param>
+ /// <param name="w">W component of the quaternion (real part).</param>
public Quat(real_t x, real_t y, real_t z, real_t w)
{
this.x = x;
@@ -209,21 +339,31 @@ namespace Godot
this.w = w;
}
- public bool IsNormalized()
- {
- return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
- }
-
+ /// <summary>
+ /// Constructs a quaternion from the given quaternion.
+ /// </summary>
+ /// <param name="q">The existing quaternion.</param>
public Quat(Quat q)
{
this = q;
}
+ /// <summary>
+ /// Constructs a quaternion from the given <see cref="Basis"/>.
+ /// </summary>
+ /// <param name="basis">The basis to construct from.</param>
public Quat(Basis basis)
{
this = basis.Quat();
}
+ /// <summary>
+ /// Constructs a quaternion that will perform a rotation specified by
+ /// Euler angles (in the YXZ convention: when decomposing,
+ /// first Z, then X, and Y last),
+ /// given in the vector format as (X angle, Y angle, Z angle).
+ /// </summary>
+ /// <param name="eulerYXZ"></param>
public Quat(Vector3 eulerYXZ)
{
real_t half_a1 = eulerYXZ.y * 0.5f;
@@ -247,11 +387,19 @@ namespace Godot
w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
}
+ /// <summary>
+ /// Constructs a quaternion that will rotate around the given axis
+ /// by the specified angle. The axis must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="angle">The angle to rotate, in radians.</param>
public Quat(Vector3 axis, real_t angle)
{
#if DEBUG
if (!axis.IsNormalized())
+ {
throw new ArgumentException("Argument is not normalized", nameof(axis));
+ }
#endif
real_t d = axis.Length();
@@ -364,6 +512,12 @@ namespace Godot
return x == other.x && y == other.y && z == other.z && w == other.w;
}
+ /// <summary>
+ /// Returns true if this quaternion and `other` are approximately equal, by running
+ /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other quaternion to compare.</param>
+ /// <returns>Whether or not the quaternions are approximately equal.</returns>
public bool IsEqualApprox(Quat other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index 1098ffe4e5..f7703c77cc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -8,6 +8,10 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// 2D axis-aligned bounding box. Rect2 consists of a position, a size, and
+ /// several utility functions. It is typically used for fast overlap tests.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Rect2 : IEquatable<Rect2>
@@ -15,29 +19,52 @@ namespace Godot
private Vector2 _position;
private Vector2 _size;
+ /// <summary>
+ /// Beginning corner. Typically has values lower than End.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector2 Position
{
get { return _position; }
set { _position = value; }
}
+ /// <summary>
+ /// Size from Position to End. Typically all components are positive.
+ /// If the size is negative, you can use <see cref="Abs"/> to fix it.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector2 Size
{
get { return _size; }
set { _size = value; }
}
+ /// <summary>
+ /// Ending corner. This is calculated as <see cref="Position"/> plus
+ /// <see cref="Size"/>. Setting this value will change the size.
+ /// </summary>
+ /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
public Vector2 End
{
get { return _position + _size; }
set { _size = value - _position; }
}
+ /// <summary>
+ /// The area of this rect.
+ /// </summary>
+ /// <value>Equivalent to <see cref="GetArea()"/>.</value>
public real_t Area
{
get { return GetArea(); }
}
+ /// <summary>
+ /// Returns a Rect2 with equivalent position and size, modified so that
+ /// the top-left corner is the origin and width and height are positive.
+ /// </summary>
+ /// <returns>The modified rect.</returns>
public Rect2 Abs()
{
Vector2 end = End;
@@ -45,12 +72,19 @@ namespace Godot
return new Rect2(topLeft, _size.Abs());
}
+ /// <summary>
+ /// Returns the intersection of this Rect2 and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The clipped rect.</returns>
public Rect2 Clip(Rect2 b)
{
var newRect = b;
if (!Intersects(newRect))
+ {
return new Rect2();
+ }
newRect._position.x = Mathf.Max(b._position.x, _position.x);
newRect._position.y = Mathf.Max(b._position.y, _position.y);
@@ -64,6 +98,11 @@ namespace Godot
return newRect;
}
+ /// <summary>
+ /// Returns true if this Rect2 completely encloses another one.
+ /// </summary>
+ /// <param name="b">The other rect that may be enclosed.</param>
+ /// <returns>A bool for whether or not this rect encloses `b`.</returns>
public bool Encloses(Rect2 b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
@@ -71,6 +110,11 @@ namespace Godot
b._position.y + b._size.y < _position.y + _size.y;
}
+ /// <summary>
+ /// Returns this Rect2 expanded to include a given point.
+ /// </summary>
+ /// <param name="to">The point to include.</param>
+ /// <returns>The expanded rect.</returns>
public Rect2 Expand(Vector2 to)
{
var expanded = this;
@@ -79,14 +123,22 @@ namespace Godot
Vector2 end = expanded._position + expanded._size;
if (to.x < begin.x)
+ {
begin.x = to.x;
+ }
if (to.y < begin.y)
+ {
begin.y = to.y;
+ }
if (to.x > end.x)
+ {
end.x = to.x;
+ }
if (to.y > end.y)
+ {
end.y = to.y;
+ }
expanded._position = begin;
expanded._size = end - begin;
@@ -94,11 +146,20 @@ namespace Godot
return expanded;
}
+ /// <summary>
+ /// Returns the area of the Rect2.
+ /// </summary>
+ /// <returns>The area.</returns>
public real_t GetArea()
{
return _size.x * _size.y;
}
+ /// <summary>
+ /// Returns a copy of the Rect2 grown a given amount of units towards all the sides.
+ /// </summary>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2 Grow(real_t by)
{
var g = this;
@@ -111,6 +172,14 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2 grown a given amount of units towards each direction individually.
+ /// </summary>
+ /// <param name="left">The amount to grow by on the left.</param>
+ /// <param name="top">The amount to grow by on the top.</param>
+ /// <param name="right">The amount to grow by on the right.</param>
+ /// <param name="bottom">The amount to grow by on the bottom.</param>
+ /// <returns>The grown rect.</returns>
public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
{
var g = this;
@@ -123,6 +192,12 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2 grown a given amount of units towards the <see cref="Margin"/> direction.
+ /// </summary>
+ /// <param name="margin">The direction to grow in.</param>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2 GrowMargin(Margin margin, real_t by)
{
var g = this;
@@ -135,11 +210,20 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns true if the Rect2 is flat or empty, or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the rect has area.</returns>
public bool HasNoArea()
{
return _size.x <= 0 || _size.y <= 0;
}
+ /// <summary>
+ /// Returns true if the Rect2 contains a point, or false otherwise.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the rect contains `point`.</returns>
public bool HasPoint(Vector2 point)
{
if (point.x < _position.x)
@@ -155,20 +239,65 @@ namespace Godot
return true;
}
- public bool Intersects(Rect2 b)
+ /// <summary>
+ /// Returns true if the Rect2 overlaps with `b`
+ /// (i.e. they have at least one point in common).
+ ///
+ /// If `includeBorders` is true, they will also be considered overlapping
+ /// if their borders touch, even without intersection.
+ /// </summary>
+ /// <param name="b">The other rect to check for intersections with.</param>
+ /// <param name="includeBorders">Whether or not to consider borders.</param>
+ /// <returns>A bool for whether or not they are intersecting.</returns>
+ public bool Intersects(Rect2 b, bool includeBorders = false)
{
- if (_position.x >= b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x <= b._position.x)
- return false;
- if (_position.y >= b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y <= b._position.y)
- return false;
+ if (includeBorders)
+ {
+ if (_position.x > b._position.x + b._size.x)
+ {
+ return false;
+ }
+ if (_position.x + _size.x < b._position.x)
+ {
+ return false;
+ }
+ if (_position.y > b._position.y + b._size.y)
+ {
+ return false;
+ }
+ if (_position.y + _size.y < b._position.y)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (_position.x >= b._position.x + b._size.x)
+ {
+ return false;
+ }
+ if (_position.x + _size.x <= b._position.x)
+ {
+ return false;
+ }
+ if (_position.y >= b._position.y + b._size.y)
+ {
+ return false;
+ }
+ if (_position.y + _size.y <= b._position.y)
+ {
+ return false;
+ }
+ }
return true;
}
+ /// <summary>
+ /// Returns a larger Rect2 that contains this Rect2 and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The merged rect.</returns>
public Rect2 Merge(Rect2 b)
{
Rect2 newRect;
@@ -179,27 +308,53 @@ namespace Godot
newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x);
newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y);
- newRect._size = newRect._size - newRect._position; // Make relative again
+ newRect._size -= newRect._position; // Make relative again
return newRect;
}
- // Constructors
+ /// <summary>
+ /// Constructs a Rect2 from a position and size.
+ /// </summary>
+ /// <param name="position">The position.</param>
+ /// <param name="size">The size.</param>
public Rect2(Vector2 position, Vector2 size)
{
_position = position;
_size = size;
}
+
+ /// <summary>
+ /// Constructs a Rect2 from a position, width, and height.
+ /// </summary>
+ /// <param name="position">The position.</param>
+ /// <param name="width">The width.</param>
+ /// <param name="height">The height.</param>
public Rect2(Vector2 position, real_t width, real_t height)
{
_position = position;
_size = new Vector2(width, height);
}
+
+ /// <summary>
+ /// Constructs a Rect2 from x, y, and size.
+ /// </summary>
+ /// <param name="x">The position's X coordinate.</param>
+ /// <param name="y">The position's Y coordinate.</param>
+ /// <param name="size">The size.</param>
public Rect2(real_t x, real_t y, Vector2 size)
{
_position = new Vector2(x, y);
_size = size;
}
+
+ /// <summary>
+ /// Constructs a Rect2 from x, y, width, and height.
+ /// </summary>
+ /// <param name="x">The position's X coordinate.</param>
+ /// <param name="y">The position's Y coordinate.</param>
+ /// <param name="width">The width.</param>
+ /// <param name="height">The height.</param>
public Rect2(real_t x, real_t y, real_t width, real_t height)
{
_position = new Vector2(x, y);
@@ -231,6 +386,12 @@ namespace Godot
return _position.Equals(other._position) && _size.Equals(other._size);
}
+ /// <summary>
+ /// Returns true if this rect and `other` are approximately equal, by running
+ /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other rect to compare.</param>
+ /// <returns>Whether or not the rects are approximately equal.</returns>
public bool IsEqualApprox(Rect2 other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
index c0b236c524..8f71c00d76 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
@@ -3,6 +3,10 @@ using System.Runtime.InteropServices;
namespace Godot
{
+ /// <summary>
+ /// 2D axis-aligned bounding box using integers. Rect2i consists of a position, a size, and
+ /// several utility functions. It is typically used for fast overlap tests.
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Rect2i : IEquatable<Rect2i>
@@ -10,29 +14,52 @@ namespace Godot
private Vector2i _position;
private Vector2i _size;
+ /// <summary>
+ /// Beginning corner. Typically has values lower than End.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector2i Position
{
get { return _position; }
set { _position = value; }
}
+ /// <summary>
+ /// Size from Position to End. Typically all components are positive.
+ /// If the size is negative, you can use <see cref="Abs"/> to fix it.
+ /// </summary>
+ /// <value>Directly uses a private field.</value>
public Vector2i Size
{
get { return _size; }
set { _size = value; }
}
+ /// <summary>
+ /// Ending corner. This is calculated as <see cref="Position"/> plus
+ /// <see cref="Size"/>. Setting this value will change the size.
+ /// </summary>
+ /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
public Vector2i End
{
get { return _position + _size; }
set { _size = value - _position; }
}
+ /// <summary>
+ /// The area of this rect.
+ /// </summary>
+ /// <value>Equivalent to <see cref="GetArea()"/>.</value>
public int Area
{
get { return GetArea(); }
}
+ /// <summary>
+ /// Returns a Rect2i with equivalent position and size, modified so that
+ /// the top-left corner is the origin and width and height are positive.
+ /// </summary>
+ /// <returns>The modified rect.</returns>
public Rect2i Abs()
{
Vector2i end = End;
@@ -40,12 +67,19 @@ namespace Godot
return new Rect2i(topLeft, _size.Abs());
}
+ /// <summary>
+ /// Returns the intersection of this Rect2i and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The clipped rect.</returns>
public Rect2i Clip(Rect2i b)
{
var newRect = b;
if (!Intersects(newRect))
+ {
return new Rect2i();
+ }
newRect._position.x = Mathf.Max(b._position.x, _position.x);
newRect._position.y = Mathf.Max(b._position.y, _position.y);
@@ -59,6 +93,11 @@ namespace Godot
return newRect;
}
+ /// <summary>
+ /// Returns true if this Rect2i completely encloses another one.
+ /// </summary>
+ /// <param name="b">The other rect that may be enclosed.</param>
+ /// <returns>A bool for whether or not this rect encloses `b`.</returns>
public bool Encloses(Rect2i b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
@@ -66,6 +105,11 @@ namespace Godot
b._position.y + b._size.y < _position.y + _size.y;
}
+ /// <summary>
+ /// Returns this Rect2i expanded to include a given point.
+ /// </summary>
+ /// <param name="to">The point to include.</param>
+ /// <returns>The expanded rect.</returns>
public Rect2i Expand(Vector2i to)
{
var expanded = this;
@@ -74,14 +118,22 @@ namespace Godot
Vector2i end = expanded._position + expanded._size;
if (to.x < begin.x)
+ {
begin.x = to.x;
+ }
if (to.y < begin.y)
+ {
begin.y = to.y;
+ }
if (to.x > end.x)
+ {
end.x = to.x;
+ }
if (to.y > end.y)
+ {
end.y = to.y;
+ }
expanded._position = begin;
expanded._size = end - begin;
@@ -89,11 +141,20 @@ namespace Godot
return expanded;
}
+ /// <summary>
+ /// Returns the area of the Rect2.
+ /// </summary>
+ /// <returns>The area.</returns>
public int GetArea()
{
return _size.x * _size.y;
}
+ /// <summary>
+ /// Returns a copy of the Rect2i grown a given amount of units towards all the sides.
+ /// </summary>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2i Grow(int by)
{
var g = this;
@@ -106,6 +167,14 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2i grown a given amount of units towards each direction individually.
+ /// </summary>
+ /// <param name="left">The amount to grow by on the left.</param>
+ /// <param name="top">The amount to grow by on the top.</param>
+ /// <param name="right">The amount to grow by on the right.</param>
+ /// <param name="bottom">The amount to grow by on the bottom.</param>
+ /// <returns>The grown rect.</returns>
public Rect2i GrowIndividual(int left, int top, int right, int bottom)
{
var g = this;
@@ -118,6 +187,12 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns a copy of the Rect2i grown a given amount of units towards the <see cref="Margin"/> direction.
+ /// </summary>
+ /// <param name="margin">The direction to grow in.</param>
+ /// <param name="by">The amount to grow by.</param>
+ /// <returns>The grown rect.</returns>
public Rect2i GrowMargin(Margin margin, int by)
{
var g = this;
@@ -130,11 +205,20 @@ namespace Godot
return g;
}
+ /// <summary>
+ /// Returns true if the Rect2 is flat or empty, or false otherwise.
+ /// </summary>
+ /// <returns>A bool for whether or not the rect has area.</returns>
public bool HasNoArea()
{
return _size.x <= 0 || _size.y <= 0;
}
+ /// <summary>
+ /// Returns true if the Rect2 contains a point, or false otherwise.
+ /// </summary>
+ /// <param name="point">The point to check.</param>
+ /// <returns>A bool for whether or not the rect contains `point`.</returns>
public bool HasPoint(Vector2i point)
{
if (point.x < _position.x)
@@ -150,20 +234,49 @@ namespace Godot
return true;
}
- public bool Intersects(Rect2i b)
+ /// <summary>
+ /// Returns true if the Rect2i overlaps with `b`
+ /// (i.e. they have at least one point in common).
+ ///
+ /// If `includeBorders` is true, they will also be considered overlapping
+ /// if their borders touch, even without intersection.
+ /// </summary>
+ /// <param name="b">The other rect to check for intersections with.</param>
+ /// <param name="includeBorders">Whether or not to consider borders.</param>
+ /// <returns>A bool for whether or not they are intersecting.</returns>
+ public bool Intersects(Rect2i b, bool includeBorders = false)
{
- if (_position.x >= b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x <= b._position.x)
- return false;
- if (_position.y >= b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y <= b._position.y)
- return false;
+ if (includeBorders)
+ {
+ if (_position.x > b._position.x + b._size.x)
+ return false;
+ if (_position.x + _size.x < b._position.x)
+ return false;
+ if (_position.y > b._position.y + b._size.y)
+ return false;
+ if (_position.y + _size.y < b._position.y)
+ return false;
+ }
+ else
+ {
+ if (_position.x >= b._position.x + b._size.x)
+ return false;
+ if (_position.x + _size.x <= b._position.x)
+ return false;
+ if (_position.y >= b._position.y + b._size.y)
+ return false;
+ if (_position.y + _size.y <= b._position.y)
+ return false;
+ }
return true;
}
+ /// <summary>
+ /// Returns a larger Rect2i that contains this Rect2 and `b`.
+ /// </summary>
+ /// <param name="b">The other rect.</param>
+ /// <returns>The merged rect.</returns>
public Rect2i Merge(Rect2i b)
{
Rect2i newRect;
@@ -174,27 +287,53 @@ namespace Godot
newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x);
newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y);
- newRect._size = newRect._size - newRect._position; // Make relative again
+ newRect._size -= newRect._position; // Make relative again
return newRect;
}
- // Constructors
+ /// <summary>
+ /// Constructs a Rect2i from a position and size.
+ /// </summary>
+ /// <param name="position">The position.</param>
+ /// <param name="size">The size.</param>
public Rect2i(Vector2i position, Vector2i size)
{
_position = position;
_size = size;
}
+
+ /// <summary>
+ /// Constructs a Rect2i from a position, width, and height.
+ /// </summary>
+ /// <param name="position">The position.</param>
+ /// <param name="width">The width.</param>
+ /// <param name="height">The height.</param>
public Rect2i(Vector2i position, int width, int height)
{
_position = position;
_size = new Vector2i(width, height);
}
+
+ /// <summary>
+ /// Constructs a Rect2i from x, y, and size.
+ /// </summary>
+ /// <param name="x">The position's X coordinate.</param>
+ /// <param name="y">The position's Y coordinate.</param>
+ /// <param name="size">The size.</param>
public Rect2i(int x, int y, Vector2i size)
{
_position = new Vector2i(x, y);
_size = size;
}
+
+ /// <summary>
+ /// Constructs a Rect2i from x, y, width, and height.
+ /// </summary>
+ /// <param name="x">The position's X coordinate.</param>
+ /// <param name="y">The position's Y coordinate.</param>
+ /// <param name="width">The width.</param>
+ /// <param name="height">The height.</param>
public Rect2i(int x, int y, int width, int height)
{
_position = new Vector2i(x, y);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index 41b4e9367f..bd1dbc1229 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -440,7 +440,12 @@ namespace Godot
// </summary>
public static bool IsAbsPath(this string instance)
{
- return System.IO.Path.IsPathRooted(instance);
+ if (string.IsNullOrEmpty(instance))
+ return false;
+ else if (instance.Length > 1)
+ return instance[0] == '/' || instance[0] == '\\' || instance.Contains(":/") || instance.Contains(":\\");
+ else
+ return instance[0] == '/' || instance[0] == '\\';
}
// <summary>
@@ -448,7 +453,7 @@ namespace Godot
// </summary>
public static bool IsRelPath(this string instance)
{
- return !System.IO.Path.IsPathRooted(instance);
+ return !IsAbsPath(instance);
}
// <summary>
@@ -624,41 +629,46 @@ namespace Godot
return instance.Length;
}
- // <summary>
- // Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'.
- // </summary>
- public static bool ExprMatch(this string instance, string expr, bool caseSensitive)
+ /// <summary>
+ /// Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'.
+ /// </summary>
+ private static bool ExprMatch(this string instance, string expr, bool caseSensitive)
{
- if (expr.Length == 0 || instance.Length == 0)
- return false;
+ // case '\0':
+ if (expr.Length == 0)
+ return instance.Length == 0;
switch (expr[0])
{
- case '\0':
- return instance[0] == 0;
case '*':
- return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive);
+ return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive));
case '?':
- return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive);
+ return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
default:
- return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) &&
- ExprMatch(expr + 1, instance + 1, caseSensitive);
+ if (instance.Length == 0) return false;
+ return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
}
}
- // <summary>
- // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]).
- // </summary>
+ /// <summary>
+ /// Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]).
+ /// </summary>
public static bool Match(this string instance, string expr, bool caseSensitive = true)
{
+ if (instance.Length == 0 || expr.Length == 0)
+ return false;
+
return instance.ExprMatch(expr, caseSensitive);
}
- // <summary>
- // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
- // </summary>
+ /// <summary>
+ /// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
+ /// </summary>
public static bool MatchN(this string instance, string expr)
{
+ if (instance.Length == 0 || expr.Length == 0)
+ return false;
+
return instance.ExprMatch(expr, caseSensitive: false);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
index 6a58b90561..ac47f6029f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
@@ -8,11 +8,28 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// 3×4 matrix (3 rows, 4 columns) used for 3D linear transformations.
+ /// It can represent transformations such as translation, rotation, or scaling.
+ /// It consists of a <see cref="Basis"/> (first 3 columns) and a
+ /// <see cref="Vector3"/> for the origin (last column).
+ ///
+ /// For more information, read this documentation article:
+ /// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Transform : IEquatable<Transform>
{
+ /// <summary>
+ /// The <see cref="Basis"/> of this transform. Contains the X, Y, and Z basis
+ /// vectors (columns 0 to 2) and is responsible for rotation and scale.
+ /// </summary>
public Basis basis;
+
+ /// <summary>
+ /// The origin vector (column 3, the fourth column). Equivalent to array index `[3]`.
+ /// </summary>
public Vector3 origin;
/// <summary>
@@ -85,13 +102,24 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation, scaling, and translation.
+ /// </summary>
+ /// <returns>The inverse transformation matrix.</returns>
public Transform AffineInverse()
{
Basis basisInv = basis.Inverse();
return new Transform(basisInv, basisInv.Xform(-origin));
}
- public Transform InterpolateWith(Transform transform, real_t c)
+ /// <summary>
+ /// Interpolates this transform to the other `transform` by `weight`.
+ /// </summary>
+ /// <param name="transform">The other transform.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated transform.</returns>
+ public Transform InterpolateWith(Transform transform, real_t weight)
{
/* not sure if very "efficient" but good enough? */
@@ -104,18 +132,37 @@ namespace Godot
Vector3 destinationLocation = transform.origin;
var interpolated = new Transform();
- interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, c).Normalized(), sourceScale.Lerp(destinationScale, c));
- interpolated.origin = sourceLocation.Lerp(destinationLocation, c);
+ interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, weight).Normalized(), sourceScale.Lerp(destinationScale, weight));
+ interpolated.origin = sourceLocation.Lerp(destinationLocation, weight);
return interpolated;
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation and translation
+ /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
+ /// </summary>
+ /// <returns>The inverse matrix.</returns>
public Transform Inverse()
{
Basis basisTr = basis.Transposed();
return new Transform(basisTr, basisTr.Xform(-origin));
}
+ /// <summary>
+ /// Returns a copy of the transform rotated such that its
+ /// -Z axis (forward) points towards the target position.
+ ///
+ /// The transform will first be rotated around the given up vector,
+ /// and then fully aligned to the target by a further rotation around
+ /// an axis perpendicular to both the target and up vectors.
+ ///
+ /// Operations take place in global space.
+ /// </summary>
+ /// <param name="target">The object to look at.</param>
+ /// <param name="up">The relative up direction</param>
+ /// <returns>The resulting transform.</returns>
public Transform LookingAt(Vector3 target, Vector3 up)
{
var t = this;
@@ -123,22 +170,39 @@ namespace Godot
return t;
}
+ /// <summary>
+ /// Returns the transform with the basis orthogonal (90 degrees),
+ /// and normalized axis vectors (scale of 1 or -1).
+ /// </summary>
+ /// <returns>The orthonormalized transform.</returns>
public Transform Orthonormalized()
{
return new Transform(basis.Orthonormalized(), origin);
}
+ /// <summary>
+ /// Rotates the transform around the given `axis` by `phi` (in radians),
+ /// using matrix multiplication. The axis must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <returns>The rotated transformation matrix.</returns>
public Transform Rotated(Vector3 axis, real_t phi)
{
return new Transform(new Basis(axis, phi), new Vector3()) * this;
}
+ /// <summary>
+ /// Scales the transform by the given 3D scaling factor, using matrix multiplication.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled transformation matrix.</returns>
public Transform Scaled(Vector3 scale)
{
return new Transform(basis.Scaled(scale), origin * scale);
}
- public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
+ private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
// Z vector
@@ -161,16 +225,30 @@ namespace Godot
origin = eye;
}
- public Transform Translated(Vector3 ofs)
+ /// <summary>
+ /// Translates the transform by the given `offset`,
+ /// relative to the transform's basis vectors.
+ ///
+ /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
+ /// this does not use matrix multiplication.
+ /// </summary>
+ /// <param name="offset">The offset to translate by.</param>
+ /// <returns>The translated matrix.</returns>
+ public Transform Translated(Vector3 offset)
{
return new Transform(basis, new Vector3
(
- origin[0] += basis.Row0.Dot(ofs),
- origin[1] += basis.Row1.Dot(ofs),
- origin[2] += basis.Row2.Dot(ofs)
+ origin[0] += basis.Row0.Dot(offset),
+ origin[1] += basis.Row1.Dot(offset),
+ origin[2] += basis.Row2.Dot(offset)
));
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by this transformation matrix.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector3 Xform(Vector3 v)
{
return new Vector3
@@ -181,6 +259,14 @@ namespace Godot
);
}
+ /// <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>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector3 XformInv(Vector3 v)
{
Vector3 vInv = v - origin;
@@ -199,24 +285,58 @@ namespace Godot
private static readonly Transform _flipY = new Transform(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero);
private static readonly Transform _flipZ = new Transform(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero);
+ /// <summary>
+ /// The identity transform, with no translation, rotation, or scaling applied.
+ /// This is used as a replacement for `Transform()` in GDScript.
+ /// Do not use `new Transform()` with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value>
public static Transform Identity { get { return _identity; } }
+ /// <summary>
+ /// The transform that will flip something along the X axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value>
public static Transform FlipX { get { return _flipX; } }
+ /// <summary>
+ /// The transform that will flip something along the Y axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)`.</value>
public static Transform FlipY { get { return _flipY; } }
+ /// <summary>
+ /// The transform that will flip something along the Z axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)`.</value>
public static Transform FlipZ { get { return _flipZ; } }
- // Constructors
+ /// <summary>
+ /// Constructs a transformation matrix from 4 vectors (matrix columns).
+ /// </summary>
+ /// <param name="column0">The X vector, or column index 0.</param>
+ /// <param name="column1">The Y vector, or column index 1.</param>
+ /// <param name="column2">The Z vector, or column index 2.</param>
+ /// <param name="origin">The origin vector, or column index 3.</param>
public Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin)
{
basis = new Basis(column0, column1, column2);
this.origin = origin;
}
+ /// <summary>
+ /// Constructs a transformation matrix from the given quaternion and origin vector.
+ /// </summary>
+ /// <param name="quat">The <see cref="Godot.Quat"/> to create the basis from.</param>
+ /// <param name="origin">The origin vector, or column index 3.</param>
public Transform(Quat quat, Vector3 origin)
{
basis = new Basis(quat);
this.origin = origin;
}
+ /// <summary>
+ /// Constructs a transformation matrix from the given basis and origin vector.
+ /// </summary>
+ /// <param name="basis">The <see cref="Godot.Basis"/> to create the basis from.</param>
+ /// <param name="origin">The origin vector, or column index 3.</param>
public Transform(Basis basis, Vector3 origin)
{
this.basis = basis;
@@ -255,6 +375,12 @@ namespace Godot
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
+ /// <summary>
+ /// Returns true if this transform and `other` are approximately equal, by running
+ /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other transform to compare.</param>
+ /// <returns>Whether or not the matrices are approximately equal.</returns>
public bool IsEqualApprox(Transform other)
{
return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 3ae96d4922..06bbe98497 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -8,25 +8,44 @@ using real_t = System.Single;
namespace Godot
{
+ /// <summary>
+ /// 2×3 matrix (2 rows, 3 columns) used for 2D linear transformations.
+ /// It can represent transformations such as translation, rotation, or scaling.
+ /// It consists of a three <see cref="Vector2"/> values: x, y, and the origin.
+ ///
+ /// For more information, read this documentation article:
+ /// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html
+ /// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Transform2D : IEquatable<Transform2D>
{
+ /// <summary>
+ /// The basis matrix's X vector (column 0). Equivalent to array index `[0]`.
+ /// </summary>
+ /// <value></value>
public Vector2 x;
+
+ /// <summary>
+ /// The basis matrix's Y vector (column 1). Equivalent to array index `[1]`.
+ /// </summary>
public Vector2 y;
+
+ /// <summary>
+ /// The origin vector (column 2, the third column). Equivalent to array index `[2]`.
+ /// The origin vector represents translation.
+ /// </summary>
public Vector2 origin;
+ /// <summary>
+ /// The rotation of this transformation matrix.
+ /// </summary>
+ /// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value>
public real_t Rotation
{
get
{
- real_t det = BasisDeterminant();
- Transform2D t = Orthonormalized();
- if (det < 0)
- {
- t.ScaleBasis(new Vector2(1, -1));
- }
- return Mathf.Atan2(t.x.y, t.x.x);
+ return Mathf.Atan2(x.y, x.x);
}
set
{
@@ -38,6 +57,10 @@ namespace Godot
}
}
+ /// <summary>
+ /// The scale of this transformation matrix.
+ /// </summary>
+ /// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value>
public Vector2 Scale
{
get
@@ -47,8 +70,7 @@ namespace Godot
}
set
{
- x = x.Normalized();
- y = y.Normalized();
+ value /= Scale; // Value becomes what's called "delta_scale" in core.
x *= value.x;
y *= value.y;
}
@@ -112,6 +134,11 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation, scaling, and translation.
+ /// </summary>
+ /// <returns>The inverse transformation matrix.</returns>
public Transform2D AffineInverse()
{
real_t det = BasisDeterminant();
@@ -135,28 +162,58 @@ namespace Godot
return inv;
}
+ /// <summary>
+ /// Returns the determinant of the basis matrix. If the basis is
+ /// uniformly scaled, its determinant is the square of the scale.
+ ///
+ /// A negative determinant means the Y scale is negative.
+ /// A zero determinant means the basis isn't invertible,
+ /// and is usually considered invalid.
+ /// </summary>
+ /// <returns>The determinant of the basis matrix.</returns>
private real_t BasisDeterminant()
{
return x.x * y.y - x.y * y.x;
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the basis matrix.
+ /// This method does not account for translation (the origin vector).
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
public Vector2 BasisXform(Vector2 v)
{
return new Vector2(Tdotx(v), Tdoty(v));
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by the inverse basis matrix.
+ /// This method does not account for translation (the origin vector).
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// basis matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector2 BasisXformInv(Vector2 v)
{
return new Vector2(x.Dot(v), y.Dot(v));
}
- public Transform2D InterpolateWith(Transform2D m, real_t c)
+ /// <summary>
+ /// Interpolates this transform to the other `transform` by `weight`.
+ /// </summary>
+ /// <param name="transform">The other transform.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated transform.</returns>
+ public Transform2D InterpolateWith(Transform2D transform, real_t weight)
{
real_t r1 = Rotation;
- real_t r2 = m.Rotation;
+ real_t r2 = transform.Rotation;
Vector2 s1 = Scale;
- Vector2 s2 = m.Scale;
+ Vector2 s2 = transform.Scale;
// Slerp rotation
var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
@@ -172,28 +229,34 @@ namespace Godot
if (dot > 0.9995f)
{
// Linearly interpolate to avoid numerical precision issues
- v = v1.Lerp(v2, c).Normalized();
+ v = v1.Lerp(v2, weight).Normalized();
}
else
{
- real_t angle = c * Mathf.Acos(dot);
+ real_t angle = weight * Mathf.Acos(dot);
Vector2 v3 = (v2 - v1 * dot).Normalized();
v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle);
}
// Extract parameters
Vector2 p1 = origin;
- Vector2 p2 = m.origin;
+ Vector2 p2 = transform.origin;
// Construct matrix
- var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.Lerp(p2, c));
- Vector2 scale = s1.Lerp(s2, c);
+ var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.Lerp(p2, weight));
+ Vector2 scale = s1.Lerp(s2, weight);
res.x *= scale;
res.y *= scale;
return res;
}
+ /// <summary>
+ /// Returns the inverse of the transform, under the assumption that
+ /// the transformation is composed of rotation and translation
+ /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
+ /// </summary>
+ /// <returns>The inverse matrix.</returns>
public Transform2D Inverse()
{
var inv = this;
@@ -208,6 +271,11 @@ namespace Godot
return inv;
}
+ /// <summary>
+ /// Returns the transform with the basis orthogonal (90 degrees),
+ /// and normalized axis vectors (scale of 1 or -1).
+ /// </summary>
+ /// <returns>The orthonormalized transform.</returns>
public Transform2D Orthonormalized()
{
var on = this;
@@ -225,11 +293,21 @@ namespace Godot
return on;
}
+ /// <summary>
+ /// Rotates the transform by `phi` (in radians), using matrix multiplication.
+ /// </summary>
+ /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <returns>The rotated transformation matrix.</returns>
public Transform2D Rotated(real_t phi)
{
return this * new Transform2D(phi, new Vector2());
}
+ /// <summary>
+ /// Scales the transform by the given scaling factor, using matrix multiplication.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled transformation matrix.</returns>
public Transform2D Scaled(Vector2 scale)
{
var copy = this;
@@ -257,6 +335,15 @@ namespace Godot
return this[0, 1] * with[0] + this[1, 1] * with[1];
}
+ /// <summary>
+ /// Translates the transform by the given `offset`,
+ /// relative to the transform's basis vectors.
+ ///
+ /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
+ /// this does not use matrix multiplication.
+ /// </summary>
+ /// <param name="offset">The offset to translate by.</param>
+ /// <returns>The translated matrix.</returns>
public Transform2D Translated(Vector2 offset)
{
var copy = this;
@@ -264,11 +351,21 @@ namespace Godot
return copy;
}
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by this transformation matrix.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
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>
+ /// <param name="v">A vector to inversely transform.</param>
+ /// <returns>The inversely transformed vector.</returns>
public Vector2 XformInv(Vector2 v)
{
Vector2 vInv = v - origin;
@@ -280,11 +377,30 @@ namespace Godot
private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0);
private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0);
- public static Transform2D Identity => _identity;
- public static Transform2D FlipX => _flipX;
- public static Transform2D FlipY => _flipY;
+ /// <summary>
+ /// The identity transform, with no translation, rotation, or scaling applied.
+ /// This is used as a replacement for `Transform2D()` in GDScript.
+ /// Do not use `new Transform2D()` with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)`.</value>
+ public static Transform2D Identity { get { return _identity; } }
+ /// <summary>
+ /// The transform that will flip something along the X axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)`.</value>
+ public static Transform2D FlipX { get { return _flipX; } }
+ /// <summary>
+ /// The transform that will flip something along the Y axis.
+ /// </summary>
+ /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)`.</value>
+ public static Transform2D FlipY { get { return _flipY; } }
- // Constructors
+ /// <summary>
+ /// Constructs a transformation matrix from 3 vectors (matrix columns).
+ /// </summary>
+ /// <param name="xAxis">The X vector, or column index 0.</param>
+ /// <param name="yAxis">The Y vector, or column index 1.</param>
+ /// <param name="originPos">The origin vector, or column index 2.</param>
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos)
{
x = xAxis;
@@ -292,7 +408,16 @@ namespace Godot
origin = originPos;
}
- // Arguments are named such that xy is equal to calling x.y
+ /// <summary>
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling x.y
+ /// </summary>
+ /// <param name="xx">The X component of the X column vector, accessed via `t.x.x` or `[0][0]`</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via `t.x.y` or `[0][1]`</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via `t.y.x` or `[1][0]`</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via `t.y.y` or `[1][1]`</param>
+ /// <param name="ox">The X component of the origin vector, accessed via `t.origin.x` or `[2][0]`</param>
+ /// <param name="oy">The Y component of the origin vector, accessed via `t.origin.y` or `[2][1]`</param>
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
{
x = new Vector2(xx, xy);
@@ -300,6 +425,11 @@ namespace Godot
origin = new Vector2(ox, oy);
}
+ /// <summary>
+ /// Constructs a transformation matrix from a rotation value and origin vector.
+ /// </summary>
+ /// <param name="rot">The rotation of the new transform, in radians.</param>
+ /// <param name="pos">The origin vector, or column index 2.</param>
public Transform2D(real_t rot, Vector2 pos)
{
x.x = y.y = Mathf.Cos(rot);
@@ -345,6 +475,12 @@ namespace Godot
return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
}
+ /// <summary>
+ /// Returns true if this transform and `other` are approximately equal, by running
+ /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other transform to compare.</param>
+ /// <returns>Whether or not the matrices are approximately equal.</returns>
public bool IsEqualApprox(Transform2D other)
{
return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 7e4804f9fd..3dff37279b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -21,15 +21,29 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector2 : IEquatable<Vector2>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
Y
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public real_t x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public real_t y;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value>
public real_t this[int index]
{
get
@@ -76,41 +90,80 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
public Vector2 Abs()
{
return new Vector2(Mathf.Abs(x), Mathf.Abs(y));
}
+ /// <summary>
+ /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians.
+ ///
+ /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when
+ /// called with the vector's `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`.
+ /// </summary>
+ /// <returns>The angle of this vector, in radians.</returns>
public real_t Angle()
{
return Mathf.Atan2(y, x);
}
+ /// <summary>
+ /// Returns the angle to the given vector, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleTo(Vector2 to)
{
return Mathf.Atan2(Cross(to), Dot(to));
}
+ /// <summary>
+ /// Returns the angle between the line connecting the two points and the X axis, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleToPoint(Vector2 to)
{
return Mathf.Atan2(y - to.y, x - to.x);
}
+ /// <summary>
+ /// Returns the aspect ratio of this vector, the ratio of `x` to `y`.
+ /// </summary>
+ /// <returns>The `x` component divided by the `y` component.</returns>
public real_t Aspect()
{
return x / y;
}
- public Vector2 Bounce(Vector2 n)
+ /// <summary>
+ /// Returns the vector "bounced off" from a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to bounce off. Must be normalized.</param>
+ /// <returns>The bounced vector.</returns>
+ public Vector2 Bounce(Vector2 normal)
{
- return -Reflect(n);
+ return -Reflect(normal);
}
+ /// <summary>
+ /// Returns a new vector with all components rounded up (towards positive infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
public Vector2 Ceil()
{
return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y));
}
+ /// <summary>
+ /// Returns the vector with a maximum length by limiting its length to `length`.
+ /// </summary>
+ /// <param name="length">The length to limit to.</param>
+ /// <returns>The vector with its length limited.</returns>
public Vector2 Clamped(real_t length)
{
var v = this;
@@ -125,17 +178,30 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns the cross product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>The cross product value.</returns>
public real_t Cross(Vector2 b)
{
return x * b.y - y * b.x;
}
+ /// <summary>
+ /// Performs a cubic interpolation between vectors `preA`, this vector, `b`, and `postB`, by the given amount `t`.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after `b`.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t)
{
- var p0 = preA;
- var p1 = this;
- var p2 = b;
- var p3 = postB;
+ Vector2 p0 = preA;
+ Vector2 p1 = this;
+ Vector2 p2 = b;
+ Vector2 p3 = postB;
real_t t2 = t * t;
real_t t3 = t2 * t;
@@ -146,46 +212,102 @@ namespace Godot
(-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
}
+ /// <summary>
+ /// Returns the normalized vector pointing from this vector to `b`.
+ /// </summary>
+ /// <param name="b">The other vector to point towards.</param>
+ /// <returns>The direction from this vector to `b`.</returns>
public Vector2 DirectionTo(Vector2 b)
{
return new Vector2(b.x - x, b.y - y).Normalized();
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `to`.
+ /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
+ /// you need to compare vectors or need the squared distance for some formula.
+ /// </summary>
+ /// <param name="to">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
public real_t DistanceSquaredTo(Vector2 to)
{
return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y);
}
+ /// <summary>
+ /// Returns the distance between this vector and `to`.
+ /// </summary>
+ /// <param name="to">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector2 to)
{
return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y));
}
+ /// <summary>
+ /// Returns the dot product of this vector and `with`.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public real_t Dot(Vector2 with)
{
return x * with.x + y * with.y;
}
+ /// <summary>
+ /// Returns a new vector with all components rounded down (towards negative infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
public Vector2 Floor()
{
return new Vector2(Mathf.Floor(x), Mathf.Floor(y));
}
+ /// <summary>
+ /// Returns the inverse of this vector. This is the same as `new Vector2(1 / v.x, 1 / v.y)`.
+ /// </summary>
+ /// <returns>The inverse of this vector.</returns>
+ public Vector2 Inverse()
+ {
+ return new Vector2(1 / x, 1 / y);
+ }
+
+ /// <summary>
+ /// Returns true if the vector is normalized, and false otherwise.
+ /// </summary>
+ /// <returns>A bool indicating whether or not the vector is normalized.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
return Mathf.Sqrt(x * x + y * y);
}
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of this vector.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare vectors or need the squared length for some formula.
+ /// </summary>
+ /// <returns>The squared length of this vector.</returns>
public real_t LengthSquared()
{
return x * x + y * y;
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector2 Lerp(Vector2 to, real_t weight)
{
return new Vector2
@@ -195,6 +317,13 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by the vector amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector2 Lerp(Vector2 to, Vector2 weight)
{
return new Vector2
@@ -204,6 +333,32 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
+ public Axis MaxAxis()
+ {
+ return x < y ? Axis.Y : Axis.X;
+ }
+
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.Y"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
+ public Axis MinAxis()
+ {
+ return x < y ? Axis.X : Axis.Y;
+ }
+
+ /// <summary>
+ /// Moves this vector toward `to` by the fixed `delta` amount.
+ /// </summary>
+ /// <param name="to">The vector to move towards.</param>
+ /// <param name="delta">The amount to move towards by.</param>
+ /// <returns>The resulting vector.</returns>
public Vector2 MoveToward(Vector2 to, real_t delta)
{
var v = this;
@@ -212,6 +367,10 @@ namespace Godot
return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
}
+ /// <summary>
+ /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`.
+ /// </summary>
+ /// <returns>A normalized version of the vector.</returns>
public Vector2 Normalized()
{
var v = this;
@@ -219,6 +378,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns>
public Vector2 PosMod(real_t mod)
{
Vector2 v;
@@ -227,6 +391,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns>
public Vector2 PosMod(Vector2 modv)
{
Vector2 v;
@@ -235,27 +404,59 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns this vector projected onto another vector `b`.
+ /// </summary>
+ /// <param name="onNormal">The vector to project onto.</param>
+ /// <returns>The projected vector.</returns>
public Vector2 Project(Vector2 onNormal)
{
return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
}
- public Vector2 Reflect(Vector2 n)
+ /// <summary>
+ /// Returns this vector reflected from a plane defined by the given `normal`.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
+ /// <returns>The reflected vector.</returns>
+ public Vector2 Reflect(Vector2 normal)
{
- return 2 * Dot(n) * n - this;
+#if DEBUG
+ if (!normal.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(normal));
+ }
+#endif
+ return 2 * Dot(normal) * normal - this;
}
+ /// <summary>
+ /// Rotates this vector by `phi` radians.
+ /// </summary>
+ /// <param name="phi">The angle to rotate by, in radians.</param>
+ /// <returns>The rotated vector.</returns>
public Vector2 Rotated(real_t phi)
{
real_t rads = Angle() + phi;
return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length();
}
+ /// <summary>
+ /// Returns this vector with all components rounded to the nearest integer,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <returns>The rounded vector.</returns>
public Vector2 Round()
{
return new Vector2(Mathf.Round(x), Mathf.Round(y));
}
+ /// <summary>
+ /// Returns a vector with each component set to one or negative one, depending
+ /// on the signs of this vector's components, or zero if the component is zero,
+ /// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
public Vector2 Sign()
{
Vector2 v;
@@ -264,23 +465,57 @@ namespace Godot
return v;
}
- public Vector2 Slerp(Vector2 b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ ///
+ /// Note: Both vectors must be normalized.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation. Must be normalized.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
+ public Vector2 Slerp(Vector2 to, real_t weight)
{
- real_t theta = AngleTo(b);
- return Rotated(theta * t);
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Vector2.Slerp: From vector is not normalized.");
+ }
+ if (!to.IsNormalized())
+ {
+ throw new InvalidOperationException("Vector2.Slerp: `to` is not normalized.");
+ }
+#endif
+ return Rotated(AngleTo(to) * weight);
}
- public Vector2 Slide(Vector2 n)
+ /// <summary>
+ /// Returns this vector slid along a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to slide on.</param>
+ /// <returns>The slid vector.</returns>
+ public Vector2 Slide(Vector2 normal)
{
- return this - n * Dot(n);
+ return this - normal * Dot(normal);
}
- public Vector2 Snapped(Vector2 by)
+ /// <summary>
+ /// Returns this vector with each component snapped to the nearest multiple of `step`.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public Vector2 Snapped(Vector2 step)
{
- return new Vector2(Mathf.Stepify(x, by.x), Mathf.Stepify(y, by.y));
+ return new Vector2(Mathf.Stepify(x, step.x), Mathf.Stepify(y, step.y));
}
- public Vector2 Tangent()
+ /// <summary>
+ /// Returns a perpendicular vector rotated 90 degrees counter-clockwise
+ /// compared to the original, with the same length.
+ /// </summary>
+ /// <returns>The perpendicular vector.</returns>
+ public Vector2 Perpendicular()
{
return new Vector2(y, -x);
}
@@ -288,7 +523,6 @@ namespace Godot
// Constants
private static readonly Vector2 _zero = new Vector2(0, 0);
private static readonly Vector2 _one = new Vector2(1, 1);
- private static readonly Vector2 _negOne = new Vector2(-1, -1);
private static readonly Vector2 _inf = new Vector2(Mathf.Inf, Mathf.Inf);
private static readonly Vector2 _up = new Vector2(0, -1);
@@ -296,22 +530,58 @@ namespace Godot
private static readonly Vector2 _right = new Vector2(1, 0);
private static readonly Vector2 _left = new Vector2(-1, 0);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(0, 0)`</value>
public static Vector2 Zero { get { return _zero; } }
- public static Vector2 NegOne { get { return _negOne; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(1, 1)`</value>
public static Vector2 One { get { return _one; } }
+ /// <summary>
+ /// Infinity vector, a vector with all components set to `Mathf.Inf`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(Mathf.Inf, Mathf.Inf)`</value>
public static Vector2 Inf { get { return _inf; } }
+ /// <summary>
+ /// Up unit vector. Y is down in 2D, so this vector points -Y.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(0, -1)`</value>
public static Vector2 Up { get { return _up; } }
+ /// <summary>
+ /// Down unit vector. Y is down in 2D, so this vector points +Y.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(0, 1)`</value>
public static Vector2 Down { get { return _down; } }
+ /// <summary>
+ /// Right unit vector. Represents the direction of right.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(1, 0)`</value>
public static Vector2 Right { get { return _right; } }
+ /// <summary>
+ /// Left unit vector. Represents the direction of left.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2(-1, 0)`</value>
public static Vector2 Left { get { return _left; } }
- // Constructors
+ /// <summary>
+ /// Constructs a new <see cref="Vector2"/> with the given components.
+ /// </summary>
+ /// <param name="x">The vector's X component.</param>
+ /// <param name="y">The vector's Y component.</param>
public Vector2(real_t x, real_t y)
{
this.x = x;
this.y = y;
}
+
+ /// <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;
@@ -400,41 +670,37 @@ namespace Godot
public static bool operator <(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y < right.y;
}
-
return left.x < right.x;
}
public static bool operator >(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y > right.y;
}
-
return left.x > right.x;
}
public static bool operator <=(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y <= right.y;
}
-
return left.x <= right.x;
}
public static bool operator >=(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y >= right.y;
}
-
return left.x >= right.x;
}
@@ -444,7 +710,6 @@ namespace Godot
{
return Equals((Vector2)obj);
}
-
return false;
}
@@ -453,6 +718,12 @@ namespace Godot
return x == other.x && y == other.y;
}
+ /// <summary>
+ /// Returns true if this vector and `other` are approximately equal, by running
+ /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other vector to compare.</param>
+ /// <returns>Whether or not the vectors are approximately equal.</returns>
public bool IsEqualApprox(Vector2 other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index 7dc22d7918..8dd9ab2f0d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -16,15 +16,29 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector2i : IEquatable<Vector2i>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
Y
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public int x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public int y;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value>
public int this[int index]
{
get
@@ -55,56 +69,102 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
public Vector2i Abs()
{
return new Vector2i(Mathf.Abs(x), Mathf.Abs(y));
}
+ /// <summary>
+ /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians.
+ ///
+ /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when
+ /// called with the vector's `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`.
+ /// </summary>
+ /// <returns>The angle of this vector, in radians.</returns>
public real_t Angle()
{
return Mathf.Atan2(y, x);
}
+ /// <summary>
+ /// Returns the angle to the given vector, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleTo(Vector2i to)
{
return Mathf.Atan2(Cross(to), Dot(to));
}
+ /// <summary>
+ /// Returns the angle between the line connecting the two points and the X axis, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleToPoint(Vector2i to)
{
return Mathf.Atan2(y - to.y, x - to.x);
}
+ /// <summary>
+ /// Returns the aspect ratio of this vector, the ratio of `x` to `y`.
+ /// </summary>
+ /// <returns>The `x` component divided by the `y` component.</returns>
public real_t Aspect()
{
return x / (real_t)y;
}
- public Vector2i Bounce(Vector2i n)
- {
- return -Reflect(n);
- }
-
+ /// <summary>
+ /// Returns the cross product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>The cross product vector.</returns>
public int Cross(Vector2i b)
{
return x * b.y - y * b.x;
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `b`.
+ /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
+ /// you need to compare vectors or need the squared distance for some formula.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
public int DistanceSquaredTo(Vector2i b)
{
return (b - this).LengthSquared();
}
+ /// <summary>
+ /// Returns the distance between this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector2i b)
{
return (b - this).Length();
}
+ /// <summary>
+ /// Returns the dot product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public int Dot(Vector2i b)
{
return x * b.x + y * b.y;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
int x2 = x * x;
@@ -113,6 +173,12 @@ namespace Godot
return Mathf.Sqrt(x2 + y2);
}
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of this vector.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare vectors or need the squared length for some formula.
+ /// </summary>
+ /// <returns>The squared length of this vector.</returns>
public int LengthSquared()
{
int x2 = x * x;
@@ -121,16 +187,31 @@ namespace Godot
return x2 + y2;
}
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
public Axis MaxAxis()
{
return x < y ? Axis.Y : Axis.X;
}
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If both components are equal, this method returns <see cref="Axis.Y"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
public Axis MinAxis()
{
- return x > y ? Axis.Y : Axis.X;
+ return x < y ? Axis.X : Axis.Y;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns>
public Vector2i PosMod(int mod)
{
Vector2i v = this;
@@ -139,6 +220,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns>
public Vector2i PosMod(Vector2i modv)
{
Vector2i v = this;
@@ -147,11 +233,12 @@ namespace Godot
return v;
}
- public Vector2i Reflect(Vector2i n)
- {
- return 2 * Dot(n) * n - this;
- }
-
+ /// <summary>
+ /// Returns a vector with each component set to one or negative one, depending
+ /// on the signs of this vector's components, or zero if the component is zero,
+ /// by calling <see cref="Mathf.Sign(int)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
public Vector2i Sign()
{
Vector2i v = this;
@@ -160,9 +247,14 @@ namespace Godot
return v;
}
- public Vector2i Tangent()
+ /// <summary>
+ /// Returns a vector rotated 90 degrees counter-clockwise
+ /// compared to the original, with the same length.
+ /// </summary>
+ /// <returns>The perpendicular vector.</returns>
+ public Vector2 Perpendicular()
{
- return new Vector2i(y, -x);
+ return new Vector2(y, -x);
}
// Constants
@@ -174,25 +266,64 @@ namespace Godot
private static readonly Vector2i _right = new Vector2i(1, 0);
private static readonly Vector2i _left = new Vector2i(-1, 0);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(0, 0)`</value>
public static Vector2i Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(1, 1)`</value>
public static Vector2i One { get { return _one; } }
+ /// <summary>
+ /// Up unit vector. Y is down in 2D, so this vector points -Y.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(0, -1)`</value>
public static Vector2i Up { get { return _up; } }
+ /// <summary>
+ /// Down unit vector. Y is down in 2D, so this vector points +Y.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(0, 1)`</value>
public static Vector2i Down { get { return _down; } }
+ /// <summary>
+ /// Right unit vector. Represents the direction of right.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(1, 0)`</value>
public static Vector2i Right { get { return _right; } }
+ /// <summary>
+ /// Left unit vector. Represents the direction of left.
+ /// </summary>
+ /// <value>Equivalent to `new Vector2i(-1, 0)`</value>
public static Vector2i Left { get { return _left; } }
- // Constructors
+ /// <summary>
+ /// Constructs a new <see cref="Vector2i"/> with the given components.
+ /// </summary>
+ /// <param name="x">The vector's X component.</param>
+ /// <param name="y">The vector's Y component.</param>
public Vector2i(int x, int y)
{
this.x = x;
this.y = y;
}
+
+ /// <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);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index b26e17ecba..4a4a2a43cd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -21,6 +21,10 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector3 : IEquatable<Vector3>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
@@ -28,10 +32,23 @@ namespace Godot
Z
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public real_t x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public real_t y;
+ /// <summary>
+ /// The vector's Z component. Also accessible by using the index position `[2]`.
+ /// </summary>
public real_t z;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value>
public real_t this[int index]
{
get
@@ -84,26 +101,49 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
public Vector3 Abs()
{
return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z));
}
+ /// <summary>
+ /// Returns the minimum angle to the given vector, in radians.
+ /// </summary>
+ /// <param name="to">The other vector to compare this vector to.</param>
+ /// <returns>The angle between the two vectors, in radians.</returns>
public real_t AngleTo(Vector3 to)
{
return Mathf.Atan2(Cross(to).Length(), Dot(to));
}
- public Vector3 Bounce(Vector3 n)
+ /// <summary>
+ /// Returns this vector "bounced off" from a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to bounce off. Must be normalized.</param>
+ /// <returns>The bounced vector.</returns>
+ public Vector3 Bounce(Vector3 normal)
{
- return -Reflect(n);
+ return -Reflect(normal);
}
+ /// <summary>
+ /// Returns a new vector with all components rounded up (towards positive infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
public Vector3 Ceil()
{
return new Vector3(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z));
}
+ /// <summary>
+ /// Returns the cross product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>The cross product vector.</returns>
public Vector3 Cross(Vector3 b)
{
return new Vector3
@@ -114,12 +154,21 @@ namespace Godot
);
}
+ /// <summary>
+ /// Performs a cubic interpolation between vectors `preA`, this vector,
+ /// `b`, and `postB`, by the given amount `t`.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after `b`.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t)
{
- var p0 = preA;
- var p1 = this;
- var p2 = b;
- var p3 = postB;
+ Vector3 p0 = preA;
+ Vector3 p1 = this;
+ Vector3 p2 = b;
+ Vector3 p3 = postB;
real_t t2 = t * t;
real_t t3 = t2 * t;
@@ -131,41 +180,79 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the normalized vector pointing from this vector to `b`.
+ /// </summary>
+ /// <param name="b">The other vector to point towards.</param>
+ /// <returns>The direction from this vector to `b`.</returns>
public Vector3 DirectionTo(Vector3 b)
{
return new Vector3(b.x - x, b.y - y, b.z - z).Normalized();
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `b`.
+ /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
+ /// you need to compare vectors or need the squared distance for some formula.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
public real_t DistanceSquaredTo(Vector3 b)
{
return (b - this).LengthSquared();
}
+ /// <summary>
+ /// Returns the distance between this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector3 b)
{
return (b - this).Length();
}
+ /// <summary>
+ /// Returns the dot product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public real_t Dot(Vector3 b)
{
return x * b.x + y * b.y + z * b.z;
}
+ /// <summary>
+ /// Returns a new vector with all components rounded down (towards negative infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
public Vector3 Floor()
{
return new Vector3(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z));
}
+ /// <summary>
+ /// Returns the inverse of this vector. This is the same as `new Vector3(1 / v.x, 1 / v.y, 1 / v.z)`.
+ /// </summary>
+ /// <returns>The inverse of this vector.</returns>
public Vector3 Inverse()
{
- return new Vector3(1.0f / x, 1.0f / y, 1.0f / z);
+ return new Vector3(1 / x, 1 / y, 1 / z);
}
+ /// <summary>
+ /// Returns true if the vector is normalized, and false otherwise.
+ /// </summary>
+ /// <returns>A bool indicating whether or not the vector is normalized.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
real_t x2 = x * x;
@@ -175,6 +262,12 @@ namespace Godot
return Mathf.Sqrt(x2 + y2 + z2);
}
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of this vector.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare vectors or need the squared length for some formula.
+ /// </summary>
+ /// <returns>The squared length of this vector.</returns>
public real_t LengthSquared()
{
real_t x2 = x * x;
@@ -184,6 +277,13 @@ namespace Godot
return x2 + y2 + z2;
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector3 Lerp(Vector3 to, real_t weight)
{
return new Vector3
@@ -194,6 +294,13 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and `to` by the vector amount `weight`.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
public Vector3 Lerp(Vector3 to, Vector3 weight)
{
return new Vector3
@@ -204,24 +311,44 @@ namespace Godot
);
}
- public Vector3 MoveToward(Vector3 to, real_t delta)
- {
- var v = this;
- var vd = to - v;
- var len = vd.Length();
- return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
- }
-
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
public Axis MaxAxis()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.Z"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
public Axis MinAxis()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
+ /// <summary>
+ /// Moves this vector toward `to` by the fixed `delta` amount.
+ /// </summary>
+ /// <param name="to">The vector to move towards.</param>
+ /// <param name="delta">The amount to move towards by.</param>
+ /// <returns>The resulting vector.</returns>
+ public Vector3 MoveToward(Vector3 to, real_t delta)
+ {
+ var v = this;
+ var vd = to - v;
+ var len = vd.Length();
+ return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
+ }
+
+ /// <summary>
+ /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`.
+ /// </summary>
+ /// <returns>A normalized version of the vector.</returns>
public Vector3 Normalized()
{
var v = this;
@@ -229,6 +356,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns the outer product with `b`.
+ /// </summary>
+ /// <param name="b">The other vector.</param>
+ /// <returns>A <see cref="Basis"/> representing the outer product matrix.</returns>
public Basis Outer(Vector3 b)
{
return new Basis(
@@ -238,6 +370,11 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns>
public Vector3 PosMod(real_t mod)
{
Vector3 v;
@@ -247,6 +384,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns>
public Vector3 PosMod(Vector3 modv)
{
Vector3 v;
@@ -256,30 +398,66 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns this vector projected onto another vector `b`.
+ /// </summary>
+ /// <param name="onNormal">The vector to project onto.</param>
+ /// <returns>The projected vector.</returns>
public Vector3 Project(Vector3 onNormal)
{
return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
}
- public Vector3 Reflect(Vector3 n)
+ /// <summary>
+ /// Returns this vector reflected from a plane defined by the given `normal`.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
+ /// <returns>The reflected vector.</returns>
+ public Vector3 Reflect(Vector3 normal)
{
#if DEBUG
- if (!n.IsNormalized())
- throw new ArgumentException("Argument is not normalized", nameof(n));
+ if (!normal.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(normal));
+ }
#endif
- return 2.0f * n * Dot(n) - this;
+ return 2.0f * Dot(normal) * normal - this;
}
- public Vector3 Round()
+ /// <summary>
+ /// Rotates this vector around a given `axis` vector by `phi` radians.
+ /// The `axis` vector must be a normalized vector.
+ /// </summary>
+ /// <param name="axis">The vector to rotate around. Must be normalized.</param>
+ /// <param name="phi">The angle to rotate by, in radians.</param>
+ /// <returns>The rotated vector.</returns>
+ public Vector3 Rotated(Vector3 axis, real_t phi)
{
- return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z));
+#if DEBUG
+ if (!axis.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(axis));
+ }
+#endif
+ return new Basis(axis, phi).Xform(this);
}
- public Vector3 Rotated(Vector3 axis, real_t phi)
+ /// <summary>
+ /// Returns this vector with all components rounded to the nearest integer,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <returns>The rounded vector.</returns>
+ public Vector3 Round()
{
- return new Basis(axis, phi).Xform(this);
+ return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z));
}
+ /// <summary>
+ /// Returns a vector with each component set to one or negative one, depending
+ /// on the signs of this vector's components, or zero if the component is zero,
+ /// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
public Vector3 Sign()
{
Vector3 v;
@@ -289,44 +467,76 @@ namespace Godot
return v;
}
- public Vector3 Slerp(Vector3 b, real_t t)
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this vector and `to` by amount `weight`.
+ ///
+ /// Note: Both vectors must be normalized.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation. Must be normalized.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
+ public Vector3 Slerp(Vector3 to, real_t weight)
{
#if DEBUG
if (!IsNormalized())
- throw new InvalidOperationException("Vector3 is not normalized");
+ {
+ throw new InvalidOperationException("Vector3.Slerp: From vector is not normalized.");
+ }
+ if (!to.IsNormalized())
+ {
+ throw new InvalidOperationException("Vector3.Slerp: `to` is not normalized.");
+ }
#endif
- real_t theta = AngleTo(b);
- return Rotated(Cross(b), theta * t);
+ real_t theta = AngleTo(to);
+ return Rotated(Cross(to), theta * weight);
}
- public Vector3 Slide(Vector3 n)
+ /// <summary>
+ /// Returns this vector slid along a plane defined by the given normal.
+ /// </summary>
+ /// <param name="normal">The normal vector defining the plane to slide on.</param>
+ /// <returns>The slid vector.</returns>
+ public Vector3 Slide(Vector3 normal)
{
- return this - n * Dot(n);
+ return this - normal * Dot(normal);
}
- public Vector3 Snapped(Vector3 by)
+ /// <summary>
+ /// Returns this vector with each component snapped to the nearest multiple of `step`.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public Vector3 Snapped(Vector3 step)
{
return new Vector3
(
- Mathf.Stepify(x, by.x),
- Mathf.Stepify(y, by.y),
- Mathf.Stepify(z, by.z)
+ Mathf.Stepify(x, step.x),
+ Mathf.Stepify(y, step.y),
+ Mathf.Stepify(z, step.z)
);
}
+ /// <summary>
+ /// Returns a diagonal matrix with the vector as main diagonal.
+ ///
+ /// This is equivalent to a Basis with no rotation or shearing and
+ /// this vector's components set as the scale.
+ /// </summary>
+ /// <returns>A Basis with the vector as its main diagonal.</returns>
public Basis ToDiagonalMatrix()
{
return new Basis(
- x, 0f, 0f,
- 0f, y, 0f,
- 0f, 0f, z
+ x, 0, 0,
+ 0, y, 0,
+ 0, 0, z
);
}
// Constants
private static readonly Vector3 _zero = new Vector3(0, 0, 0);
private static readonly Vector3 _one = new Vector3(1, 1, 1);
- private static readonly Vector3 _negOne = new Vector3(-1, -1, -1);
private static readonly Vector3 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf);
private static readonly Vector3 _up = new Vector3(0, 1, 0);
@@ -336,25 +546,74 @@ namespace Godot
private static readonly Vector3 _forward = new Vector3(0, 0, -1);
private static readonly Vector3 _back = new Vector3(0, 0, 1);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 0, 0)`</value>
public static Vector3 Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(1, 1, 1)`</value>
public static Vector3 One { get { return _one; } }
- public static Vector3 NegOne { get { return _negOne; } }
+ /// <summary>
+ /// Infinity vector, a vector with all components set to `Mathf.Inf`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf)`</value>
public static Vector3 Inf { get { return _inf; } }
+ /// <summary>
+ /// Up unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 1, 0)`</value>
public static Vector3 Up { get { return _up; } }
+ /// <summary>
+ /// Down unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, -1, 0)`</value>
public static Vector3 Down { get { return _down; } }
+ /// <summary>
+ /// Right unit vector. Represents the local direction of right,
+ /// and the global direction of east.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(1, 0, 0)`</value>
public static Vector3 Right { get { return _right; } }
+ /// <summary>
+ /// Left unit vector. Represents the local direction of left,
+ /// and the global direction of west.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(-1, 0, 0)`</value>
public static Vector3 Left { get { return _left; } }
+ /// <summary>
+ /// Forward unit vector. Represents the local direction of forward,
+ /// and the global direction of north.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 0, -1)`</value>
public static Vector3 Forward { get { return _forward; } }
+ /// <summary>
+ /// Back unit vector. Represents the local direction of back,
+ /// and the global direction of south.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3(0, 0, 1)`</value>
public static Vector3 Back { get { return _back; } }
- // Constructors
+ /// <summary>
+ /// Constructs a new <see cref="Vector3"/> with the given components.
+ /// </summary>
+ /// <param name="x">The vector's X component.</param>
+ /// <param name="y">The vector's Y component.</param>
+ /// <param name="z">The vector's Z component.</param>
public Vector3(real_t x, real_t y, real_t z)
{
this.x = x;
this.y = y;
this.z = z;
}
+
+ /// <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;
@@ -454,49 +713,53 @@ namespace Godot
public static bool operator <(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z < right.z;
+ }
return left.y < right.y;
}
-
return left.x < right.x;
}
public static bool operator >(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z > right.z;
+ }
return left.y > right.y;
}
-
return left.x > right.x;
}
public static bool operator <=(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z <= right.z;
+ }
return left.y < right.y;
}
-
return left.x < right.x;
}
public static bool operator >=(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z >= right.z;
+ }
return left.y > right.y;
}
-
return left.x > right.x;
}
@@ -515,6 +778,12 @@ namespace Godot
return x == other.x && y == other.y && z == other.z;
}
+ /// <summary>
+ /// Returns true if this vector and `other` are approximately equal, by running
+ /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other vector to compare.</param>
+ /// <returns>Whether or not the vectors are approximately equal.</returns>
public bool IsEqualApprox(Vector3 other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index c17f900131..bf25ba9cb3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -16,6 +16,10 @@ namespace Godot
[StructLayout(LayoutKind.Sequential)]
public struct Vector3i : IEquatable<Vector3i>
{
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// </summary>
public enum Axis
{
X = 0,
@@ -23,10 +27,23 @@ namespace Godot
Z
}
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// </summary>
public int x;
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// </summary>
public int y;
+ /// <summary>
+ /// The vector's Z component. Also accessible by using the index position `[2]`.
+ /// </summary>
public int z;
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value>
public int this[int index]
{
get
@@ -62,39 +79,51 @@ namespace Godot
}
}
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
public Vector3i Abs()
{
- Vector3i v = this;
- if (v.x < 0)
- {
- v.x = -v.x;
- }
- if (v.y < 0)
- {
- v.y = -v.y;
- }
- if (v.z < 0)
- {
- v.z = -v.z;
- }
- return v;
+ return new Vector3i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z));
}
+ /// <summary>
+ /// Returns the squared distance between this vector and `b`.
+ /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
+ /// you need to compare vectors or need the squared distance for some formula.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
public int DistanceSquaredTo(Vector3i b)
{
return (b - this).LengthSquared();
}
+ /// <summary>
+ /// Returns the distance between this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector3i b)
{
return (b - this).Length();
}
+ /// <summary>
+ /// Returns the dot product of this vector and `b`.
+ /// </summary>
+ /// <param name="b">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
public int Dot(Vector3i b)
{
return x * b.x + y * b.y + z * b.z;
}
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <returns>The length of this vector.</returns>
public real_t Length()
{
int x2 = x * x;
@@ -104,6 +133,12 @@ namespace Godot
return Mathf.Sqrt(x2 + y2 + z2);
}
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of this vector.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare vectors or need the squared length for some formula.
+ /// </summary>
+ /// <returns>The squared length of this vector.</returns>
public int LengthSquared()
{
int x2 = x * x;
@@ -113,16 +148,31 @@ namespace Godot
return x2 + y2 + z2;
}
+ /// <summary>
+ /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the largest axis.</returns>
public Axis MaxAxis()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
+ /// <summary>
+ /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.Z"/>.
+ /// </summary>
+ /// <returns>The index of the smallest axis.</returns>
public Axis MinAxis()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns>
public Vector3i PosMod(int mod)
{
Vector3i v = this;
@@ -132,6 +182,11 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns>
public Vector3i PosMod(Vector3i modv)
{
Vector3i v = this;
@@ -141,6 +196,12 @@ namespace Godot
return v;
}
+ /// <summary>
+ /// Returns a vector with each component set to one or negative one, depending
+ /// on the signs of this vector's components, or zero if the component is zero,
+ /// by calling <see cref="Mathf.Sign(int)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
public Vector3i Sign()
{
Vector3i v = this;
@@ -161,29 +222,81 @@ namespace Godot
private static readonly Vector3i _forward = new Vector3i(0, 0, -1);
private static readonly Vector3i _back = new Vector3i(0, 0, 1);
+ /// <summary>
+ /// Zero vector, a vector with all components set to `0`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, 0, 0)`</value>
public static Vector3i Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to `1`.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(1, 1, 1)`</value>
public static Vector3i One { get { return _one; } }
+ /// <summary>
+ /// Up unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, 1, 0)`</value>
public static Vector3i Up { get { return _up; } }
+ /// <summary>
+ /// Down unit vector.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, -1, 0)`</value>
public static Vector3i Down { get { return _down; } }
+ /// <summary>
+ /// Right unit vector. Represents the local direction of right,
+ /// and the global direction of east.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(1, 0, 0)`</value>
public static Vector3i Right { get { return _right; } }
+ /// <summary>
+ /// Left unit vector. Represents the local direction of left,
+ /// and the global direction of west.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(-1, 0, 0)`</value>
public static Vector3i Left { get { return _left; } }
+ /// <summary>
+ /// Forward unit vector. Represents the local direction of forward,
+ /// and the global direction of north.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, 0, -1)`</value>
public static Vector3i Forward { get { return _forward; } }
+ /// <summary>
+ /// Back unit vector. Represents the local direction of back,
+ /// and the global direction of south.
+ /// </summary>
+ /// <value>Equivalent to `new Vector3i(0, 0, 1)`</value>
public static Vector3i Back { get { return _back; } }
- // Constructors
+ /// <summary>
+ /// Constructs a new <see cref="Vector3i"/> with the given components.
+ /// </summary>
+ /// <param name="x">The vector's X component.</param>
+ /// <param name="y">The vector's Y component.</param>
+ /// <param name="z">The vector's Z component.</param>
public Vector3i(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
+
+ /// <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);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index b5ac124c9a..86a16c17f1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -1,39 +1,17 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AEBF0036-DA76-4341-B651-A3F2856AB2FA}</ProjectGuid>
- <OutputType>Library</OutputType>
<OutputPath>bin/$(Configuration)</OutputPath>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <AssemblyName>GodotSharp</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFramework>netstandard2.1</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
- <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
+ <EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>portable</DebugType>
- <Optimize>false</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>portable</DebugType>
- <Optimize>true</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
+ <PropertyGroup>
+ <DefineConstants>$(DefineConstants);GODOT</DefineConstants>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
- <Reference Include="System" />
- </ItemGroup>
- <ItemGroup>
<Compile Include="Core\AABB.cs" />
<Compile Include="Core\Array.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
@@ -53,6 +31,7 @@
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.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" />
@@ -89,5 +68,4 @@
Fortunately code completion, go to definition and such still work.
-->
<Import Project="Generated\GeneratedIncludes.props" />
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs
index f84e0183f6..da6f293871 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs
@@ -1,27 +1,3 @@
-using System.Reflection;
using System.Runtime.CompilerServices;
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("GodotSharp")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("1.0.*")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
[assembly: InternalsVisibleTo("GodotSharpEditor")]
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
index 8785931312..a8c4ba96b5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
@@ -1,46 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8FBEC238-D944-4074-8548-B3B524305905}</ProjectGuid>
- <OutputType>Library</OutputType>
<OutputPath>bin/$(Configuration)</OutputPath>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <AssemblyName>GodotSharpEditor</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFramework>netstandard2.1</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
- <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
+ <EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>portable</DebugType>
- <Optimize>false</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>portable</DebugType>
- <Optimize>true</Optimize>
- <DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <ConsolePause>false</ConsolePause>
+ <PropertyGroup>
+ <DefineConstants>$(DefineConstants);GODOT</DefineConstants>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
- <Reference Include="System" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="Properties\AssemblyInfo.cs" />
- </ItemGroup>
- <Import Project="Generated\GeneratedIncludes.props" />
- <ItemGroup>
<ProjectReference Include="..\GodotSharp\GodotSharp.csproj">
- <Private>False</Private>
+ <Private>false</Private>
</ProjectReference>
</ItemGroup>
- <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <!--
+ We import a props file with auto-generated includes. This works well with Rider.
+ However, Visual Studio and MonoDevelop won't list them in the solution explorer.
+ We can't use wildcards as there may be undesired old files still hanging around.
+ Fortunately code completion, go to definition and such still work.
+ -->
+ <Import Project="Generated\GeneratedIncludes.props" />
</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs
deleted file mode 100644
index 3684b7a3cb..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Reflection;
-
-// Information about this assembly is defined by the following attributes.
-// Change them to the values specific to your project.
-
-[assembly: AssemblyTitle("GodotSharpEditor")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-
-[assembly: AssemblyVersion("1.0.*")]
-
-// The following attributes are used to specify the signing key for the assembly,
-// if desired. See the Mono documentation for more information about signing.
-
-//[assembly: AssemblyDelaySign(false)]
-//[assembly: AssemblyKeyFile("")]
diff --git a/modules/mono/glue/arguments_vector.h b/modules/mono/glue/arguments_vector.h
index 4ee553fc11..ab48904571 100644
--- a/modules/mono/glue/arguments_vector.h
+++ b/modules/mono/glue/arguments_vector.h
@@ -40,8 +40,8 @@ private:
T *_ptr;
int size;
- ArgumentsVector();
- ArgumentsVector(const ArgumentsVector &);
+ ArgumentsVector() = delete;
+ ArgumentsVector(const ArgumentsVector &) = delete;
public:
T *ptr() { return _ptr; }
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index f370883320..ebcd6d5e9c 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "base_object_glue.h"
-
#ifdef MONO_GLUE_ENABLED
+#include "core/class_db.h"
+#include "core/object.h"
#include "core/reference.h"
#include "core/string_name.h"
@@ -39,6 +39,7 @@
#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"
@@ -140,16 +141,18 @@ MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString
}
MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
- if (!p_ptr)
+ if (!p_ptr) {
return nullptr;
+ }
Ref<WeakRef> wref;
Reference *ref = Object::cast_to<Reference>(p_ptr);
if (ref) {
REF r = ref;
- if (!r.is_valid())
+ if (!r.is_valid()) {
return nullptr;
+ }
wref.instance();
wref->set_ref(r);
@@ -241,6 +244,7 @@ void godot_register_object_icalls() {
mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor);
mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed);
mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed);
+ mono_add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", (void *)godot_icall_Object_ConnectEventSignals);
mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method);
mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString);
mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref);
diff --git a/modules/mono/glue/base_object_glue.h b/modules/mono/glue/base_object_glue.h
deleted file mode 100644
index 67769f3061..0000000000
--- a/modules/mono/glue/base_object_glue.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*************************************************************************/
-/* base_object_glue.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 BASE_OBJECT_GLUE_H
-#define BASE_OBJECT_GLUE_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/class_db.h"
-#include "core/object.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-Object *godot_icall_Object_Ctor(MonoObject *p_obj);
-
-void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr);
-
-void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer);
-
-void godot_icall_Object_ConnectEventSignals(Object *p_ptr);
-
-MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method);
-
-MonoObject *godot_icall_Object_weakref(Object *p_ptr);
-
-Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter);
-
-// DynamicGodotObject
-
-MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr);
-
-MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result);
-
-MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result);
-
-MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value);
-
-MonoString *godot_icall_Object_ToString(Object *p_ptr);
-
-// Register internal calls
-
-void godot_register_object_icalls();
-
-#endif // MONO_GLUE_ENABLED
-
-#endif // BASE_OBJECT_GLUE_H
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index 4e3dc9c4ea..3313e8cb77 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -28,14 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "collections_glue.h"
-
#ifdef MONO_GLUE_ENABLED
#include <mono/metadata/exception.h>
+#include "core/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() {
@@ -103,10 +104,31 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int 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;
+}
+
int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
}
@@ -283,6 +305,7 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) {
void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", (void *)godot_icall_Array_Ctor_MonoArray);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic);
@@ -290,6 +313,7 @@ void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", (void *)godot_icall_Array_Concatenate);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate);
diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h
deleted file mode 100644
index f8351a1fc7..0000000000
--- a/modules/mono/glue/collections_glue.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*************************************************************************/
-/* collections_glue.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 COLLECTIONS_GLUE_H
-#define COLLECTIONS_GLUE_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/array.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-// Array
-
-Array *godot_icall_Array_Ctor();
-
-void godot_icall_Array_Dtor(Array *ptr);
-
-MonoObject *godot_icall_Array_At(Array *ptr, int index);
-
-MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class);
-
-void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value);
-
-int godot_icall_Array_Count(Array *ptr);
-
-int godot_icall_Array_Add(Array *ptr, MonoObject *item);
-
-void godot_icall_Array_Clear(Array *ptr);
-
-MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item);
-
-void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index);
-
-Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep);
-
-int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item);
-
-void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item);
-
-MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item);
-
-void godot_icall_Array_RemoveAt(Array *ptr, int index);
-
-Error godot_icall_Array_Resize(Array *ptr, int new_size);
-
-void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class);
-
-MonoString *godot_icall_Array_ToString(Array *ptr);
-
-// Dictionary
-
-Dictionary *godot_icall_Dictionary_Ctor();
-
-void godot_icall_Dictionary_Dtor(Dictionary *ptr);
-
-MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key);
-
-MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class);
-
-void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value);
-
-Array *godot_icall_Dictionary_Keys(Dictionary *ptr);
-
-Array *godot_icall_Dictionary_Values(Dictionary *ptr);
-
-int godot_icall_Dictionary_Count(Dictionary *ptr);
-
-void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value);
-
-void godot_icall_Dictionary_Clear(Dictionary *ptr);
-
-MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value);
-
-MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key);
-
-Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep);
-
-MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key);
-
-MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value);
-
-MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value);
-
-MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class);
-
-void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class);
-
-MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr);
-
-// Register internal calls
-
-void godot_register_collections_icalls();
-
-#endif // MONO_GLUE_ENABLED
-
-#endif // COLLECTIONS_GLUE_H
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index e43b06d18e..5e892b616b 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -28,8 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "gd_glue.h"
-
#ifdef MONO_GLUE_ENABLED
#include "core/array.h"
@@ -40,6 +38,7 @@
#include "core/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) {
@@ -147,8 +146,9 @@ void godot_icall_GD_prints(MonoArray *p_what) {
return;
}
- if (i)
+ if (i) {
str += " ";
+ }
str += elem_str;
}
@@ -171,8 +171,9 @@ void godot_icall_GD_printt(MonoArray *p_what) {
return;
}
- if (i)
+ if (i) {
str += "\t";
+ }
str += elem_str;
}
@@ -197,7 +198,7 @@ double godot_icall_GD_rand_range(double from, double to) {
}
uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) {
- int ret = Math::rand_from_seed(&seed);
+ uint32_t ret = Math::rand_from_seed(&seed);
*newSeed = seed;
return ret;
}
@@ -213,10 +214,11 @@ MonoString *godot_icall_GD_str(MonoArray *p_what) {
for (int i = 0; i < what.size(); i++) {
String os = what[i].operator String();
- if (i == 0)
+ if (i == 0) {
str = os;
- else
+ } else {
str += os;
+ }
}
return GDMonoMarshal::mono_string_from_godot(str);
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index ee99a300b9..c1f1936711 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -30,13 +30,16 @@
#ifdef MONO_GLUE_ENABLED
-#include "base_object_glue.h"
-#include "collections_glue.h"
-#include "gd_glue.h"
-#include "nodepath_glue.h"
-#include "rid_glue.h"
-#include "string_glue.h"
-#include "string_name_glue.h"
+#include "../mono_gd/gd_mono_marshal.h"
+
+void godot_register_collections_icalls();
+void godot_register_gd_icalls();
+void godot_register_string_name_icalls();
+void godot_register_nodepath_icalls();
+void godot_register_object_icalls();
+void godot_register_rid_icalls();
+void godot_register_string_icalls();
+void godot_register_scene_tree_icalls();
/**
* Registers internal calls that were not generated. This function is called
@@ -50,6 +53,7 @@ void godot_register_glue_header_icalls() {
godot_register_object_icalls();
godot_register_rid_icalls();
godot_register_string_icalls();
+ godot_register_scene_tree_icalls();
}
// Used by the generated glue
diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp
index e413f404d8..2aa75dd309 100644
--- a/modules/mono/glue/nodepath_glue.cpp
+++ b/modules/mono/glue/nodepath_glue.cpp
@@ -28,12 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "nodepath_glue.h"
-
#ifdef MONO_GLUE_ENABLED
+#include "core/node_path.h"
#include "core/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)));
}
@@ -51,7 +52,7 @@ MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) {
return (MonoBoolean)p_ptr->is_absolute();
}
-uint32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) {
+int32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) {
return p_ptr->get_name_count();
}
@@ -59,7 +60,7 @@ 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));
}
-uint32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) {
+int32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) {
return p_ptr->get_subname_count();
}
diff --git a/modules/mono/glue/nodepath_glue.h b/modules/mono/glue/nodepath_glue.h
deleted file mode 100644
index 727679c278..0000000000
--- a/modules/mono/glue/nodepath_glue.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*************************************************************************/
-/* nodepath_glue.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 NODEPATH_GLUE_H
-#define NODEPATH_GLUE_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/node_path.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-NodePath *godot_icall_NodePath_Ctor(MonoString *p_path);
-
-void godot_icall_NodePath_Dtor(NodePath *p_ptr);
-
-MonoString *godot_icall_NodePath_operator_String(NodePath *p_np);
-
-MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr);
-
-uint32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr);
-
-MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx);
-
-uint32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr);
-
-MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx);
-
-MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr);
-
-NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr);
-
-MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr);
-
-// Register internal calls
-
-void godot_register_nodepath_icalls();
-
-#endif // MONO_GLUE_ENABLED
-
-#endif // NODEPATH_GLUE_H
diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp
index 66a49d8fec..6d2e6b559f 100644
--- a/modules/mono/glue/rid_glue.cpp
+++ b/modules/mono/glue/rid_glue.cpp
@@ -28,17 +28,20 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "rid_glue.h"
-
#ifdef MONO_GLUE_ENABLED
+#include "core/object.h"
#include "core/resource.h"
+#include "core/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)
+ if (res_from) {
return memnew(RID(res_from->get_rid()));
+ }
return memnew(RID);
}
diff --git a/modules/mono/glue/rid_glue.h b/modules/mono/glue/rid_glue.h
deleted file mode 100644
index 506d715451..0000000000
--- a/modules/mono/glue/rid_glue.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*************************************************************************/
-/* rid_glue.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 RID_GLUE_H
-#define RID_GLUE_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/object.h"
-#include "core/rid.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-RID *godot_icall_RID_Ctor(Object *p_from);
-
-void godot_icall_RID_Dtor(RID *p_ptr);
-
-uint32_t godot_icall_RID_get_id(RID *p_ptr);
-
-// Register internal calls
-
-void godot_register_rid_icalls();
-
-#endif // MONO_GLUE_ENABLED
-
-#endif // RID_GLUE_H
diff --git a/modules/mono/glue/string_name_glue.h b/modules/mono/glue/scene_tree_glue.cpp
index 88354ddd84..b43daccc1b 100644
--- a/modules/mono/glue/string_name_glue.h
+++ b/modules/mono/glue/scene_tree_glue.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* string_name_glue.h */
+/* scene_tree_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,27 +28,59 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef STRING_NAME_GLUE_H
-#define STRING_NAME_GLUE_H
-
#ifdef MONO_GLUE_ENABLED
+#include "core/array.h"
+#include "core/class_db.h"
#include "core/string_name.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"
-StringName *godot_icall_StringName_Ctor(MonoString *p_path);
+Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype) {
+ List<Node *> nodes;
+ Array ret;
-void godot_icall_StringName_Dtor(StringName *p_ptr);
+ // Retrieve all the nodes in the group
+ ptr->get_nodes_in_group(*group, &nodes);
-MonoString *godot_icall_StringName_operator_String(StringName *p_np);
+ // No need to bother if the group is empty
+ if (!nodes.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);
-MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr);
+ 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());
-// Register internal calls
+ if (si != nullptr) {
+ MonoObject *obj = si->get_mono_object();
+ if (obj != nullptr && mono_object_get_class(obj) == mono_class) {
+ ret.push_back(nodes[i]);
+ }
+ }
+ }
+ }
+ }
-void godot_register_string_name_icalls();
+ return memnew(Array(ret));
+}
-#endif // MONO_GLUE_ENABLED
+void godot_register_scene_tree_icalls() {
+ mono_add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", (void *)godot_icall_SceneTree_get_nodes_in_group_Generic);
+}
-#endif // STRING_NAME_GLUE_H
+#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp
index e407a70db9..595b8d71f1 100644
--- a/modules/mono/glue/string_glue.cpp
+++ b/modules/mono/glue/string_glue.cpp
@@ -28,14 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "string_glue.h"
-
#ifdef MONO_GLUE_ENABLED
#include "core/ustring.h"
#include "core/variant.h"
#include "core/vector.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?
diff --git a/modules/mono/glue/string_glue.h b/modules/mono/glue/string_glue.h
deleted file mode 100644
index a5e833ba61..0000000000
--- a/modules/mono/glue/string_glue.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*************************************************************************/
-/* string_glue.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 STRING_GLUE_H
-#define STRING_GLUE_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-MonoArray *godot_icall_String_md5_buffer(MonoString *p_str);
-
-MonoString *godot_icall_String_md5_text(MonoString *p_str);
-
-int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from);
-
-int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from);
-
-MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str);
-
-MonoString *godot_icall_String_sha256_text(MonoString *p_str);
-
-// Register internal calls
-
-void godot_register_string_icalls();
-
-#endif // MONO_GLUE_ENABLED
-
-#endif // STRING_GLUE_H
diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp
index 81006e5849..4b2e88569b 100644
--- a/modules/mono/glue/string_name_glue.cpp
+++ b/modules/mono/glue/string_name_glue.cpp
@@ -28,12 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "string_name_glue.h"
-
#ifdef MONO_GLUE_ENABLED
+#include "core/string_name.h"
#include "core/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)));
}
@@ -48,7 +49,7 @@ MonoString *godot_icall_StringName_operator_String(StringName *p_np) {
}
MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
- return (MonoBoolean)(p_ptr == StringName());
+ return (MonoBoolean)(*p_ptr == StringName());
}
void godot_register_string_name_icalls() {
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 692da991c7..df31823deb 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -122,7 +122,7 @@ public:
private:
_GodotSharpDirs() {
- res_data_dir = "res://.mono";
+ res_data_dir = "res://.godot/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());
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index 26347e9162..dbe9c7fc5d 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -48,8 +48,9 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus
MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle.get_target();
if (!delegate_a || !delegate_b) {
- if (!delegate_a && !delegate_b)
+ if (!delegate_a && !delegate_b) {
return true;
+ }
return false;
}
@@ -58,8 +59,9 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus
}
bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
- if (compare_equal(p_a, p_b))
+ if (compare_equal(p_a, p_b)) {
return false;
+ }
return p_a < p_b;
}
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 080f366692..cf5ac33d20 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -144,8 +144,9 @@ void gd_mono_debug_init() {
if (Engine::get_singleton()->is_editor_hint() ||
ProjectSettings::get_singleton()->get_resource_path().empty() ||
Main::is_project_manager()) {
- if (da_args.size() == 0)
+ if (da_args.size() == 0) {
return;
+ }
}
if (da_args.length() == 0) {
@@ -422,22 +423,28 @@ void GDMono::initialize_load_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 (Main::is_project_manager()) {
+ 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())
+ 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)
+ if (!out_of_sync) {
out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync;
+ }
#endif
return out_of_sync;
}
@@ -509,16 +516,17 @@ void GDMono::_init_exception_policy() {
}
}
-void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) {
+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)
+ if (p_name == "mscorlib" && corlib_assembly) {
return corlib_assembly;
+ }
MonoDomain *domain = mono_domain_get();
- uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
+ int32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
GDMonoAssembly **result = assemblies[domain_id].getptr(p_name);
return result ? *result : nullptr;
}
@@ -553,8 +561,9 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo
GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, p_refonly, p_search_dirs);
- if (!assembly)
+ if (!assembly) {
return false;
+ }
*r_assembly = assembly;
@@ -570,8 +579,9 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo
GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly);
- if (!assembly)
+ if (!assembly) {
return false;
+ }
*r_assembly = assembly;
@@ -591,16 +601,19 @@ ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMo
if (nativecalls_klass) {
GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
- if (api_hash_field)
+ if (api_hash_field) {
api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(nullptr));
+ }
GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
- if (binds_ver_field)
+ if (binds_ver_field) {
api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(nullptr));
+ }
GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
- if (cs_glue_ver_field)
+ if (cs_glue_ver_field) {
api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(nullptr));
+ }
}
return api_assembly_version;
@@ -611,13 +624,15 @@ String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) {
}
bool GDMono::_load_corlib_assembly() {
- if (corlib_assembly)
+ if (corlib_assembly) {
return true;
+ }
bool success = load_assembly("mscorlib", &corlib_assembly);
- if (success)
+ if (success) {
GDMonoCache::update_corlib_cache();
+ }
return success;
}
@@ -644,12 +659,14 @@ bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = assembly_name + ".xml";
- if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK)
+ if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) {
WARN_PRINT("Failed to copy '" + xml_file + "'.");
+ }
String pdb_file = assembly_name + ".pdb";
- if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK)
+ if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) {
WARN_PRINT("Failed to copy '" + pdb_file + "'.");
+ }
String assembly_file = assembly_name + ".dll";
if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) {
@@ -664,13 +681,15 @@ static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool
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))
+ 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");
- if (!FileAccess::exists(cached_api_hash_path))
+ if (!FileAccess::exists(cached_api_hash_path)) {
return false;
+ }
Ref<ConfigFile> cfg;
cfg.instance();
@@ -763,8 +782,9 @@ String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const
// 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))
+ 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");
@@ -792,8 +812,9 @@ String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const
#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)
+ 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
@@ -825,8 +846,9 @@ bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, c
#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)
+ 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
@@ -856,30 +878,35 @@ bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly,
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())
+ if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Mono: Failed to load Core API assembly");
+ }
return false;
}
#ifdef TOOLS_ENABLED
if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) {
- if (OS::get_singleton()->is_stdout_verbose())
+ if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Mono: Failed to load Editor API assembly");
+ }
return false;
}
- if (r_editor_api_assembly.out_of_sync)
+ 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)
+ if (r_core_api_assembly.out_of_sync) {
return false;
+ }
- if (p_callback)
+ if (p_callback) {
return p_callback();
+ }
return true;
}
@@ -887,8 +914,9 @@ bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, Lo
bool GDMono::_on_core_api_assembly_loaded() {
GDMonoCache::update_godot_api_cache();
- if (!GDMonoCache::cached_data.godot_api_cache_updated)
+ if (!GDMonoCache::cached_data.godot_api_cache_updated) {
return false;
+ }
get_singleton()->_install_trace_listener();
@@ -953,8 +981,9 @@ void GDMono::_load_api_assemblies() {
#ifdef TOOLS_ENABLED
bool GDMono::_load_tools_assemblies() {
- if (tools_assembly && tools_project_editor_assembly)
+ 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);
@@ -964,8 +993,9 @@ bool GDMono::_load_tools_assemblies() {
#endif
bool GDMono::_load_project_assembly() {
- if (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);
@@ -1017,8 +1047,9 @@ Error GDMono::_unload_scripts_domain() {
print_verbose("Mono: Finalizing scripts domain...");
- if (mono_domain_get() != root_domain)
+ if (mono_domain_get() != root_domain) {
mono_domain_set(root_domain, true);
+ }
finalizing_scripts_domain = true;
@@ -1108,8 +1139,9 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
print_verbose("Mono: Unloading domain '" + domain_name + "'...");
- if (mono_domain_get() == p_domain)
+ 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.");
@@ -1135,10 +1167,11 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
MonoImage *image = mono_class_get_image(p_raw_class);
- if (image == corlib_assembly->get_image())
+ if (image == corlib_assembly->get_image()) {
return corlib_assembly->get_class(p_raw_class);
+ }
- uint32_t domain_id = mono_domain_get_id(mono_domain_get());
+ int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
const String *k = nullptr;
@@ -1146,9 +1179,9 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
GDMonoAssembly *assembly = domain_assemblies.get(*k);
if (assembly->get_image() == image) {
GDMonoClass *klass = assembly->get_class(p_raw_class);
-
- if (klass)
+ if (klass) {
return klass;
+ }
}
}
@@ -1157,24 +1190,26 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) {
GDMonoClass *klass = corlib_assembly->get_class(p_namespace, p_name);
- if (klass)
+ if (klass) {
return klass;
+ }
- uint32_t domain_id = mono_domain_get_id(mono_domain_get());
+ int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
const String *k = nullptr;
while ((k = domain_assemblies.next(k))) {
GDMonoAssembly *assembly = domain_assemblies.get(*k);
klass = assembly->get_class(p_namespace, p_name);
- if (klass)
+ if (klass) {
return klass;
+ }
}
return nullptr;
}
-void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
+void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
const String *k = nullptr;
@@ -1192,8 +1227,9 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
#ifdef DEBUG_ENABLED
GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
- if (EngineDebugger::is_active())
+ if (EngineDebugger::is_active()) {
EngineDebugger::get_singleton()->poll_events(false);
+ }
#endif
exit(mono_environment_exitcode_get());
@@ -1268,7 +1304,7 @@ GDMono::~GDMono() {
// Leave the rest to 'mono_jit_cleanup'
#endif
- const uint32_t *k = nullptr;
+ const int32_t *k = nullptr;
while ((k = assemblies.next(k))) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k);
@@ -1292,8 +1328,9 @@ GDMono::~GDMono() {
gdmono::android::support::cleanup();
#endif
- if (gdmono_log)
+ if (gdmono_log) {
memdelete(gdmono_log);
+ }
singleton = nullptr;
}
@@ -1310,37 +1347,44 @@ void _GodotSharp::detach_thread() {
int32_t _GodotSharp::get_domain_id() {
MonoDomain *domain = mono_domain_get();
- CRASH_COND(!domain); // User must check if runtime is initialized before calling this method
+ 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();
- CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method
+ ERR_FAIL_NULL_V(domain, -1);
return mono_domain_get_id(domain);
}
bool _GodotSharp::is_scripts_domain_loaded() {
- return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != nullptr;
+ 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() {
- return is_domain_finalizing_for_unload(mono_domain_get());
-}
-
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) {
- if (!p_domain)
- return true;
- if (p_domain == GDMono::get_singleton()->get_scripts_domain() && GDMono::get_singleton()->is_finalizing_scripts_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);
}
@@ -1349,15 +1393,17 @@ bool _GodotSharp::is_runtime_shutting_down() {
}
bool _GodotSharp::is_runtime_initialized() {
- return GDMono::get_singleton()->is_runtime_initialized();
+ return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized();
}
void _GodotSharp::_reload_assemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
+ CRASH_COND(CSharpLanguage::get_singleton() == nullptr);
// This method may be called more than once with `call_deferred`, so we need to check
// again if reloading is needed to avoid reloading multiple times unnecessarily.
- if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed())
+ if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload);
+ }
#endif
}
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 833855b371..18f7418049 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -97,7 +97,7 @@ private:
MonoDomain *root_domain;
MonoDomain *scripts_domain;
- HashMap<uint32_t, HashMap<String, GDMonoAssembly *>> assemblies;
+ HashMap<int32_t, HashMap<String, GDMonoAssembly *>> assemblies;
GDMonoAssembly *corlib_assembly;
GDMonoAssembly *project_assembly;
@@ -141,7 +141,7 @@ private:
Error _unload_scripts_domain();
#endif
- void _domain_assemblies_cleanup(uint32_t p_domain_id);
+ void _domain_assemblies_cleanup(int32_t p_domain_id);
uint64_t api_core_hash;
#ifdef TOOLS_ENABLED
@@ -165,14 +165,16 @@ protected:
public:
#ifdef DEBUG_METHODS_ENABLED
uint64_t get_api_core_hash() {
- if (api_core_hash == 0)
+ if (api_core_hash == 0) {
api_core_hash = ClassDB::get_api_hash(ClassDB::API_CORE);
+ }
return api_core_hash;
}
#ifdef TOOLS_ENABLED
uint64_t get_api_editor_hash() {
- if (api_editor_hash == 0)
+ if (api_editor_hash == 0) {
api_editor_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR);
+ }
return api_editor_hash;
}
#endif // TOOLS_ENABLED
@@ -202,7 +204,7 @@ public:
UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
// Do not use these, unless you know what you're doing
- void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
+ 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 */; }
@@ -252,18 +254,18 @@ class ScopeDomain {
public:
ScopeDomain(MonoDomain *p_domain) {
- MonoDomain *prev_domain = mono_domain_get();
+ prev_domain = mono_domain_get();
if (prev_domain != p_domain) {
- this->prev_domain = prev_domain;
mono_domain_set(p_domain, false);
} else {
- this->prev_domain = nullptr;
+ prev_domain = nullptr;
}
}
~ScopeDomain() {
- if (prev_domain)
+ if (prev_domain) {
mono_domain_set(prev_domain, false);
+ }
}
};
@@ -276,8 +278,9 @@ public:
}
~ScopeExitDomainUnload() {
- if (domain)
+ if (domain) {
GDMono::get_singleton()->finalize_and_unload_domain(domain);
+ }
}
};
@@ -298,9 +301,6 @@ class _GodotSharp : public Object {
bool _is_domain_finalizing_for_unload(int32_t p_domain_id);
- List<NodePath *> np_delete_queue;
- List<RID *> rid_delete_queue;
-
void _reload_assemblies(bool p_soft_reload);
protected:
@@ -318,7 +318,6 @@ public:
bool is_scripts_domain_loaded();
- bool is_domain_finalizing_for_unload();
bool is_domain_finalizing_for_unload(int32_t p_domain_id);
bool is_domain_finalizing_for_unload(MonoDomain *p_domain);
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 073c9a5214..6e351001d4 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -107,9 +107,10 @@ void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]]
GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly));
#ifdef GD_MONO_HOT_RELOAD
- const char *path = mono_image_get_filename(image);
- if (FileAccess::exists(path))
+ 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();
@@ -137,8 +138,9 @@ MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unus
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)
+ if (loaded_asm) {
return loaded_asm->get_assembly();
+ }
return nullptr;
}
@@ -161,22 +163,25 @@ MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAs
path = search_dir.plus_file(p_name);
if (FileAccess::exists(path)) {
res = _real_load_assembly_from(path, p_refonly, p_aname);
- if (res != nullptr)
+ 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)
+ 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)
+ if (res != nullptr) {
return res;
+ }
}
}
}
@@ -194,16 +199,19 @@ String GDMonoAssembly::find_assembly(const String &p_name) {
if (has_extension) {
path = search_dir.plus_file(p_name);
- if (FileAccess::exists(path))
+ if (FileAccess::exists(path)) {
return path;
+ }
} else {
path = search_dir.plus_file(p_name + ".dll");
- if (FileAccess::exists(path))
+ if (FileAccess::exists(path)) {
return path;
+ }
path = search_dir.plus_file(p_name + ".exe");
- if (FileAccess::exists(path))
+ if (FileAccess::exists(path)) {
return path;
+ }
}
}
@@ -279,8 +287,9 @@ MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, boo
if (!FileAccess::exists(pdb_path)) {
pdb_path = p_path.get_basename() + ".pdb"; // without .dll
- if (!FileAccess::exists(pdb_path))
+ if (!FileAccess::exists(pdb_path)) {
goto no_pdb;
+ }
}
pdb_data = FileAccess::get_file_as_array(pdb_path);
@@ -307,8 +316,9 @@ no_pdb:
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)
+ if (!loaded_asm) {
assembly_load_hook(assembly, nullptr);
+ }
}
// Decrement refcount which was previously incremented by mono_image_open_from_data_with_name
@@ -342,13 +352,15 @@ GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const Stri
GDMonoClass **match = cached_classes.getptr(key);
- if (match)
+ if (match) {
return *match;
+ }
MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
- if (!mono_class)
+ if (!mono_class) {
return nullptr;
+ }
GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
@@ -363,8 +375,9 @@ GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
- if (match)
+ if (match) {
return match->value();
+ }
StringName namespace_name = mono_class_get_namespace(p_mono_class);
StringName class_name = mono_class_get_name(p_mono_class);
@@ -383,8 +396,9 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
if (gdobject_class_cache_updated) {
Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class);
- if (result)
+ if (result) {
match = result->get();
+ }
} else {
List<GDMonoClass *> nested_classes;
@@ -393,30 +407,34 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class)
for (int i = 1; i < rows; i++) {
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))
+ if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) {
continue;
+ }
GDMonoClass *current = get_class(mono_class);
- if (!current)
+ if (!current) {
continue;
+ }
nested_classes.push_back(current);
- if (!match && current->get_name() == p_class)
+ if (!match && current->get_name() == p_class) {
match = current;
+ }
while (!nested_classes.empty()) {
GDMonoClass *current_nested = nested_classes.front()->get();
- nested_classes.pop_back();
+ nested_classes.pop_front();
void *iter = nullptr;
while (true) {
MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_mono_ptr(), &iter);
- if (!raw_nested)
+ if (!raw_nested) {
break;
+ }
GDMonoClass *nested_class = get_class(raw_nested);
@@ -437,15 +455,18 @@ GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_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"))
+ 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);
- ERR_FAIL_NULL_V(assembly, nullptr);
+ if (!assembly) {
+ return nullptr;
+ }
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
@@ -456,8 +477,9 @@ GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_a
}
GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
- if (p_name == "mscorlib" || p_name == "mscorlib.dll")
+ 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());
@@ -467,7 +489,9 @@ GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_
if (!assembly) {
assembly = _real_load_assembly_from(p_path, p_refonly);
- ERR_FAIL_NULL_V(assembly, nullptr);
+ if (!assembly) {
+ return nullptr;
+ }
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
@@ -477,6 +501,7 @@ GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_
}
GDMonoAssembly::~GDMonoAssembly() {
- if (image)
+ if (image) {
unload();
+ }
}
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index c002ad2139..29aef6e609 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -217,7 +217,7 @@ void update_corlib_cache() {
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)", 1));
+ 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"));
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 691da55b10..6575cbc1c8 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -81,15 +81,17 @@ bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
StringName GDMonoClass::get_namespace() const {
GDMonoClass *nesting_class = get_nesting_class();
- if (!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)
+ if (!nesting_class) {
return class_name;
+ }
return nesting_class->get_name_for_lookup() + "/" + class_name;
}
@@ -131,11 +133,13 @@ bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
#endif
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return false;
+ }
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
@@ -145,11 +149,13 @@ MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, nullptr);
#endif
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return nullptr;
+ }
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
@@ -164,8 +170,9 @@ void GDMonoClass::fetch_attributes() {
void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) {
CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this));
- if (methods_fetched)
+ if (methods_fetched) {
return;
+ }
void *iter = nullptr;
MonoMethod *raw_method = nullptr;
@@ -202,8 +209,9 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
break;
}
- if (native_top == CACHED_CLASS(GodotObject))
+ if (native_top == CACHED_CLASS(GodotObject)) {
break;
+ }
native_top = native_top->get_parent_class();
}
@@ -212,8 +220,9 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
uint32_t flags = mono_method_get_flags(method->mono_method, nullptr);
- if (!(flags & MONO_METHOD_ATTR_VIRTUAL))
+ if (!(flags & MONO_METHOD_ATTR_VIRTUAL)) {
continue;
+ }
// Virtual method of Godot Object derived type, let's try to find GodotMethod attribute
@@ -235,15 +244,17 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
#endif
MethodKey key = MethodKey(godot_method_name, method->get_parameters_count());
GDMonoMethod **existing_method = methods.getptr(key);
- if (existing_method)
+ if (existing_method) {
memdelete(*existing_method); // Must delete old one
+ }
methods.set(key, method);
break;
}
- if (top == CACHED_CLASS(GodotObject))
+ if (top == CACHED_CLASS(GodotObject)) {
break;
+ }
top = top->get_parent_class();
}
@@ -258,8 +269,9 @@ GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p
const MethodKey *k = nullptr;
while ((k = methods.next(k))) {
- if (k->name == p_name)
+ if (k->name == p_name) {
return methods.get(*k);
+ }
}
return nullptr;
@@ -283,11 +295,13 @@ GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_cou
GDMonoMethod **match = methods.getptr(key);
- if (match)
+ if (match) {
return *match;
+ }
- if (methods_fetched)
+ 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);
@@ -323,8 +337,9 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName
GDMonoMethod **match = methods.getptr(key);
- if (match)
+ if (match) {
return *match;
+ }
GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
methods.set(key, method);
@@ -337,8 +352,9 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
mono_method_desc_free(desc);
- if (!method)
+ if (!method) {
return nullptr;
+ }
ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr);
@@ -348,11 +364,13 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
- if (result)
+ if (result) {
return result->value();
+ }
- if (fields_fetched)
+ if (fields_fetched) {
return nullptr;
+ }
MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
@@ -367,8 +385,9 @@ GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
}
const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
- if (fields_fetched)
+ if (fields_fetched) {
return fields_list;
+ }
void *iter = nullptr;
MonoClassField *raw_field = nullptr;
@@ -394,11 +413,13 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
Map<StringName, GDMonoProperty *>::Element *result = properties.find(p_name);
- if (result)
+ if (result) {
return result->value();
+ }
- if (properties_fetched)
+ if (properties_fetched) {
return nullptr;
+ }
MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data());
@@ -413,8 +434,9 @@ GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
}
const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
- if (properties_fetched)
+ if (properties_fetched) {
return properties_list;
+ }
void *iter = nullptr;
MonoProperty *raw_property = nullptr;
@@ -438,8 +460,9 @@ const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
}
const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
- if (delegates_fetched)
+ if (delegates_fetched) {
return delegates_list;
+ }
void *iter = nullptr;
MonoClass *raw_class = nullptr;
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 948170f51c..563c45e71f 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -597,11 +597,13 @@ String GDMonoField::get_string_value(MonoObject *p_object) {
bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return false;
+ }
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
@@ -609,11 +611,13 @@ bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, nullptr);
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return nullptr;
+ }
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index 27e402d777..fe1c2d28dd 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -114,7 +114,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
}
void unhandled_exception(MonoException *p_exc) {
- mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
+ mono_print_unhandled_exception((MonoObject *)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
@@ -122,9 +122,10 @@ void unhandled_exception(MonoException *p_exc) {
GD_UNREACHABLE();
} else {
#ifdef DEBUG_ENABLED
- GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
- if (EngineDebugger::is_active())
+ GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
+ if (EngineDebugger::is_active()) {
EngineDebugger::get_singleton()->poll_events(false);
+ }
#endif
}
}
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index 04728be725..b8ee0204c4 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -55,33 +55,41 @@ static int get_log_level_id(const char *p_log_level) {
int i = 0;
while (valid_log_levels[i]) {
- if (!strcmp(valid_log_levels[i], p_log_level))
+ 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 *) {
FileAccess *f = GDMonoLog::get_singleton()->log_file;
if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) {
- String text(message);
- text += " (in domain ";
- text += log_domain;
- if (log_level) {
- text += ", ";
- text += log_level;
- }
- text += ")\n";
+ String text = make_text(log_domain, log_level, message);
+ text += "\n";
f->seek_end();
f->store_string(text);
}
if (fatal) {
- ERR_PRINT("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'.");
+ 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
f->flush();
f->close();
@@ -115,10 +123,12 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
String current;
while ((current = da->get_next()).length()) {
- if (da->current_is_dir())
+ if (da->current_is_dir()) {
continue;
- if (!current.ends_with(".txt"))
+ }
+ if (!current.ends_with(".txt")) {
continue;
+ }
uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current));
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 085062261d..c460e283ea 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -72,92 +72,119 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
case MONO_TYPE_VALUETYPE: {
GDMonoClass *vtclass = p_type.type_class;
- if (vtclass == CACHED_CLASS(Vector2))
+ if (vtclass == CACHED_CLASS(Vector2)) {
return Variant::VECTOR2;
+ }
- if (vtclass == CACHED_CLASS(Vector2i))
+ if (vtclass == CACHED_CLASS(Vector2i)) {
return Variant::VECTOR2I;
+ }
- if (vtclass == CACHED_CLASS(Rect2))
+ if (vtclass == CACHED_CLASS(Rect2)) {
return Variant::RECT2;
+ }
- if (vtclass == CACHED_CLASS(Rect2i))
+ if (vtclass == CACHED_CLASS(Rect2i)) {
return Variant::RECT2I;
+ }
- if (vtclass == CACHED_CLASS(Transform2D))
+ if (vtclass == CACHED_CLASS(Transform2D)) {
return Variant::TRANSFORM2D;
+ }
- if (vtclass == CACHED_CLASS(Vector3))
+ if (vtclass == CACHED_CLASS(Vector3)) {
return Variant::VECTOR3;
+ }
- if (vtclass == CACHED_CLASS(Vector3i))
+ if (vtclass == CACHED_CLASS(Vector3i)) {
return Variant::VECTOR3I;
+ }
- if (vtclass == CACHED_CLASS(Basis))
+ if (vtclass == CACHED_CLASS(Basis)) {
return Variant::BASIS;
+ }
- if (vtclass == CACHED_CLASS(Quat))
+ if (vtclass == CACHED_CLASS(Quat)) {
return Variant::QUAT;
+ }
- if (vtclass == CACHED_CLASS(Transform))
+ if (vtclass == CACHED_CLASS(Transform)) {
return Variant::TRANSFORM;
+ }
- if (vtclass == CACHED_CLASS(AABB))
+ if (vtclass == CACHED_CLASS(AABB)) {
return Variant::AABB;
+ }
- if (vtclass == CACHED_CLASS(Color))
+ if (vtclass == CACHED_CLASS(Color)) {
return Variant::COLOR;
+ }
- if (vtclass == CACHED_CLASS(Plane))
+ if (vtclass == CACHED_CLASS(Plane)) {
return Variant::PLANE;
+ }
- if (vtclass == CACHED_CLASS(Callable))
+ if (vtclass == CACHED_CLASS(Callable)) {
return Variant::CALLABLE;
+ }
- if (vtclass == CACHED_CLASS(SignalInfo))
+ if (vtclass == CACHED_CLASS(SignalInfo)) {
return Variant::SIGNAL;
+ }
- if (mono_class_is_enum(vtclass->get_mono_ptr()))
+ if (mono_class_is_enum(vtclass->get_mono_ptr())) {
return Variant::INT;
+ }
} break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
return Variant::ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
return Variant::PACKED_BYTE_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
return Variant::PACKED_INT32_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
return Variant::PACKED_INT64_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(float))
+ if (array_type->eklass == CACHED_CLASS_RAW(float)) {
return Variant::PACKED_FLOAT32_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(double))
+ if (array_type->eklass == CACHED_CLASS_RAW(double)) {
return Variant::PACKED_FLOAT64_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(String))
+ if (array_type->eklass == CACHED_CLASS_RAW(String)) {
return Variant::PACKED_STRING_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
return Variant::PACKED_VECTOR2_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
return Variant::PACKED_VECTOR3_ARRAY;
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Color))
+ if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
return Variant::PACKED_COLOR_ARRAY;
+ }
GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class))
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
return Variant::ARRAY;
+ }
} break;
case MONO_TYPE_CLASS: {
@@ -201,8 +228,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
} break;
case MONO_TYPE_OBJECT: {
- if (r_nil_is_variant)
+ if (r_nil_is_variant) {
*r_nil_is_variant = true;
+ }
return Variant::NIL;
} break;
@@ -244,8 +272,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
} break;
}
- if (r_nil_is_variant)
+ if (r_nil_is_variant) {
*r_nil_is_variant = false;
+ }
// Unknown
return Variant::NIL;
@@ -282,68 +311,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
return false;
}
-bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) {
- switch (p_dictionary_type.type_encoding) {
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type());
-
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype) ||
- GDMonoUtils::Marshal::type_is_system_generic_dictionary(dict_reftype) ||
- GDMonoUtils::Marshal::type_is_generic_idictionary(dict_reftype)) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
-
- GDMonoUtils::Marshal::dictionary_get_key_value_types(dict_reftype, &key_reftype, &value_reftype);
-
- r_key_type = ManagedType::from_reftype(key_reftype);
- r_value_type = ManagedType::from_reftype(value_reftype);
- return true;
- }
- } break;
- default: {
- } break;
- }
-
- return false;
-}
-
-String mono_to_utf8_string(MonoString *p_mono_string) {
- MonoError error;
- char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error);
-
- if (!mono_error_ok(&error)) {
- ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&error) + "'.");
- mono_error_cleanup(&error);
- return String();
- }
-
- String ret = String::utf8(utf8);
-
- mono_free(utf8);
-
- return ret;
-}
-
-String mono_to_utf16_string(MonoString *p_mono_string) {
- int len = mono_string_length(p_mono_string);
- String ret;
-
- if (len == 0)
- return ret;
-
- ret.resize(len + 1);
- ret.set(len, 0);
-
- CharType *src = (CharType *)mono_string_chars(p_mono_string);
- CharType *dst = ret.ptrw();
-
- for (int i = 0; i < len; i++) {
- dst[i] = src[i];
- }
-
- return ret;
-}
-
MonoObject *variant_to_mono_object(const Variant *p_var) {
ManagedType type;
@@ -409,8 +376,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
case MONO_TYPE_STRING: {
- if (p_var->get_type() == Variant::NIL)
+ if (p_var->get_type() == Variant::NIL) {
return nullptr; // Otherwise, Variant -> String would return the string "Null"
+ }
return (MonoObject *)mono_string_from_godot(p_var->operator String());
} break;
@@ -547,39 +515,50 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
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))
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
return (MonoObject *)Array_to_mono_array(p_var->operator Array());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t))
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(float))
+ if (array_type->eklass == CACHED_CLASS_RAW(float)) {
return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(double))
+ if (array_type->eklass == CACHED_CLASS_RAW(double)) {
return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(String))
+ if (array_type->eklass == CACHED_CLASS_RAW(String)) {
return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
return (MonoObject *)PackedVector2Array_to_mono_array(p_var->operator PackedVector2Array());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
return (MonoObject *)PackedVector3Array_to_mono_array(p_var->operator PackedVector3Array());
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Color))
+ if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray());
+ }
GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class))
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
return (MonoObject *)Array_to_mono_array(p_var->operator Array(), array_type_class);
+ }
ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed array of unmarshallable element type.");
} break;
@@ -624,8 +603,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return BOX_BOOLEAN(val);
}
case Variant::INT: {
- int32_t val = p_var->operator signed int();
- return BOX_INT32(val);
+ int64_t val = p_var->operator int64_t();
+ return BOX_INT64(val);
}
case Variant::FLOAT: {
#ifdef REAL_T_IS_DOUBLE
@@ -820,100 +799,128 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<double>(p_obj);
case MONO_TYPE_STRING: {
- if (p_obj == nullptr)
+ if (p_obj == nullptr) {
return Variant(); // NIL
+ }
return mono_string_to_godot_not_null((MonoString *)p_obj);
} break;
case MONO_TYPE_VALUETYPE: {
GDMonoClass *vtclass = p_type.type_class;
- if (vtclass == CACHED_CLASS(Vector2))
+ if (vtclass == CACHED_CLASS(Vector2)) {
return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Vector2i))
+ if (vtclass == CACHED_CLASS(Vector2i)) {
return MARSHALLED_IN(Vector2i, unbox_addr<GDMonoMarshal::M_Vector2i>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Rect2))
+ if (vtclass == CACHED_CLASS(Rect2)) {
return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Rect2i))
+ if (vtclass == CACHED_CLASS(Rect2i)) {
return MARSHALLED_IN(Rect2i, unbox_addr<GDMonoMarshal::M_Rect2i>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Transform2D))
+ if (vtclass == CACHED_CLASS(Transform2D)) {
return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Vector3))
+ if (vtclass == CACHED_CLASS(Vector3)) {
return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Vector3i))
+ if (vtclass == CACHED_CLASS(Vector3i)) {
return MARSHALLED_IN(Vector3i, unbox_addr<GDMonoMarshal::M_Vector3i>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Basis))
+ if (vtclass == CACHED_CLASS(Basis)) {
return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Quat))
+ if (vtclass == CACHED_CLASS(Quat)) {
return MARSHALLED_IN(Quat, unbox_addr<GDMonoMarshal::M_Quat>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Transform))
+ if (vtclass == CACHED_CLASS(Transform)) {
return MARSHALLED_IN(Transform, unbox_addr<GDMonoMarshal::M_Transform>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(AABB))
+ if (vtclass == CACHED_CLASS(AABB)) {
return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Color))
+ if (vtclass == CACHED_CLASS(Color)) {
return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Plane))
+ if (vtclass == CACHED_CLASS(Plane)) {
return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(Callable))
+ if (vtclass == CACHED_CLASS(Callable)) {
return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj));
+ }
- if (vtclass == CACHED_CLASS(SignalInfo))
+ 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()))
+ if (mono_class_is_enum(vtclass->get_mono_ptr())) {
return unbox<int32_t>(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))
+ 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))
+ 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))
+ 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))
+ 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))
+ if (array_type->eklass == CACHED_CLASS_RAW(float)) {
return mono_array_to_PackedFloat32Array((MonoArray *)p_obj);
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(double))
+ if (array_type->eklass == CACHED_CLASS_RAW(double)) {
return mono_array_to_PackedFloat64Array((MonoArray *)p_obj);
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(String))
+ if (array_type->eklass == CACHED_CLASS_RAW(String)) {
return mono_array_to_PackedStringArray((MonoArray *)p_obj);
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
return mono_array_to_PackedVector2Array((MonoArray *)p_obj);
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
return mono_array_to_PackedVector3Array((MonoArray *)p_obj);
+ }
- if (array_type->eklass == CACHED_CLASS_RAW(Color))
+ if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
return mono_array_to_PackedColorArray((MonoArray *)p_obj);
+ }
GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class))
+ 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.");
@@ -1012,8 +1019,9 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
}
Variant mono_object_to_variant(MonoObject *p_obj) {
- if (!p_obj)
+ if (!p_obj) {
return Variant();
+ }
ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
@@ -1021,20 +1029,26 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
}
Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) {
- if (!p_obj)
+ 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)
+ 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);
@@ -1044,8 +1058,9 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc);
if (exc) {
- if (r_exc)
+ if (r_exc) {
*r_exc = exc;
+ }
return String();
}
@@ -1155,8 +1170,9 @@ MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_c
Array mono_array_to_Array(MonoArray *p_array) {
Array ret;
- if (!p_array)
+ if (!p_array) {
return ret;
+ }
int length = mono_array_length(p_array);
ret.resize(length);
@@ -1174,22 +1190,23 @@ MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length);
- int32_t *dst = (int32_t *)mono_array_addr(ret, int32_t, 0);
- memcpy(dst, src, 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)
+ if (!p_array) {
return ret;
+ }
int length = mono_array_length(p_array);
ret.resize(length);
int32_t *dst = ret.ptrw();
- const int32_t *src = (const int32_t *)mono_array_addr(p_array, int32_t, 0);
- memcpy(dst, src, length);
+ const int32_t *src = mono_array_addr(p_array, int32_t, 0);
+ memcpy(dst, src, length * sizeof(int32_t));
return ret;
}
@@ -1200,22 +1217,23 @@ MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length);
- int64_t *dst = (int64_t *)mono_array_addr(ret, int64_t, 0);
- memcpy(dst, src, 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)
+ if (!p_array) {
return ret;
+ }
int length = mono_array_length(p_array);
ret.resize(length);
int64_t *dst = ret.ptrw();
- const int64_t *src = (const int64_t *)mono_array_addr(p_array, int64_t, 0);
- memcpy(dst, src, length);
+ const int64_t *src = mono_array_addr(p_array, int64_t, 0);
+ memcpy(dst, src, length * sizeof(int64_t));
return ret;
}
@@ -1226,22 +1244,23 @@ MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length);
- uint8_t *dst = (uint8_t *)mono_array_addr(ret, uint8_t, 0);
- memcpy(dst, src, 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)
+ if (!p_array) {
return ret;
+ }
int length = mono_array_length(p_array);
ret.resize(length);
uint8_t *dst = ret.ptrw();
- const uint8_t *src = (const uint8_t *)mono_array_addr(p_array, uint8_t, 0);
- memcpy(dst, src, length);
+ const uint8_t *src = mono_array_addr(p_array, uint8_t, 0);
+ memcpy(dst, src, length * sizeof(uint8_t));
return ret;
}
@@ -1252,22 +1271,23 @@ MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length);
- float *dst = (float *)mono_array_addr(ret, float, 0);
- memcpy(dst, src, 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)
+ if (!p_array) {
return ret;
+ }
int length = mono_array_length(p_array);
ret.resize(length);
float *dst = ret.ptrw();
- const float *src = (const float *)mono_array_addr(p_array, float, 0);
- memcpy(dst, src, length);
+ const float *src = mono_array_addr(p_array, float, 0);
+ memcpy(dst, src, length * sizeof(float));
return ret;
}
@@ -1278,22 +1298,23 @@ MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length);
- double *dst = (double *)mono_array_addr(ret, double, 0);
- memcpy(dst, src, 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)
+ if (!p_array) {
return ret;
+ }
int length = mono_array_length(p_array);
ret.resize(length);
double *dst = ret.ptrw();
- const double *src = (const double *)mono_array_addr(p_array, double, 0);
- memcpy(dst, src, length);
+ const double *src = mono_array_addr(p_array, double, 0);
+ memcpy(dst, src, length * sizeof(double));
return ret;
}
@@ -1314,8 +1335,9 @@ MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) {
PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) {
PackedStringArray ret;
- if (!p_array)
+ if (!p_array) {
return ret;
+ }
int length = mono_array_length(p_array);
ret.resize(length);
String *w = ret.ptrw();
@@ -1335,8 +1357,8 @@ MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length);
if constexpr (InteropLayout::MATCHES_Color) {
- Color *dst = (Color *)mono_array_addr(ret, Color, 0);
- memcpy(dst, src, length);
+ 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);
@@ -1349,15 +1371,16 @@ MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) {
PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) {
PackedColorArray ret;
- if (!p_array)
+ 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 = (const Color *)mono_array_addr(p_array, Color, 0);
- memcpy(dst, src, length);
+ 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));
@@ -1374,8 +1397,8 @@ MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length);
if constexpr (InteropLayout::MATCHES_Vector2) {
- Vector2 *dst = (Vector2 *)mono_array_addr(ret, Vector2, 0);
- memcpy(dst, src, length);
+ 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);
@@ -1388,15 +1411,16 @@ MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) {
PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) {
PackedVector2Array ret;
- if (!p_array)
+ 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 = (const Vector2 *)mono_array_addr(p_array, Vector2, 0);
- memcpy(dst, src, length);
+ 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));
@@ -1413,8 +1437,8 @@ MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) {
MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length);
if constexpr (InteropLayout::MATCHES_Vector3) {
- Vector3 *dst = (Vector3 *)mono_array_addr(ret, Vector3, 0);
- memcpy(dst, src, length);
+ 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);
@@ -1427,15 +1451,16 @@ MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) {
PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) {
PackedVector3Array ret;
- if (!p_array)
+ 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 = (const Vector3 *)mono_array_addr(p_array, Vector3, 0);
- memcpy(dst, src, length);
+ 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));
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index f2d887e6d6..a1fd975916 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -66,40 +66,26 @@ T *unbox_addr(MonoObject *p_obj) {
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);
-bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type);
// String
-String mono_to_utf8_string(MonoString *p_mono_string);
-String mono_to_utf16_string(MonoString *p_mono_string);
-
_FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) {
- if (sizeof(CharType) == 2)
- return mono_to_utf16_string(p_mono_string);
-
- return mono_to_utf8_string(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)
+ if (p_mono_string == nullptr) {
return String();
+ }
return mono_string_to_godot_not_null(p_mono_string);
}
-_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) {
- return mono_string_new(mono_domain_get(), p_string.utf8().get_data());
-}
-
-_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) {
- return mono_string_from_utf16((mono_unichar2 *)p_string.c_str());
-}
-
_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
- if (sizeof(CharType) == 2)
- return mono_from_utf16_string(p_string);
-
- return mono_from_utf8_string(p_string);
+ return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data()));
}
// Variant
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index e601bb12ad..04f3b25a70 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -155,11 +155,13 @@ MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, Mono
bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return false;
+ }
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
@@ -167,11 +169,13 @@ bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, nullptr);
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return nullptr;
+ }
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
@@ -250,8 +254,9 @@ const MethodInfo &GDMonoMethod::get_method_info() {
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)
+ 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);
@@ -259,8 +264,9 @@ const MethodInfo &GDMonoMethod::get_method_info() {
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)
+ if (arg_info.type == Variant::NIL && nil_is_variant) {
arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
method_info.arguments.push_back(arg_info);
}
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index c3e7598f2d..bc3be97102 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -77,15 +77,17 @@ GDMonoProperty::~GDMonoProperty() {
bool GDMonoProperty::is_static() {
MonoMethod *prop_method = mono_property_get_get_method(mono_property);
- if (prop_method == nullptr)
+ 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)
+ 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:
@@ -106,11 +108,13 @@ IMonoClassMember::Visibility GDMonoProperty::get_visibility() {
bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, false);
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return false;
+ }
return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
}
@@ -118,11 +122,13 @@ bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) {
MonoObject *GDMonoProperty::get_attribute(GDMonoClass *p_attr_class) {
ERR_FAIL_NULL_V(p_attr_class, nullptr);
- if (!attrs_fetched)
+ if (!attrs_fetched) {
fetch_attributes();
+ }
- if (!attributes)
+ if (!attributes) {
return nullptr;
+ }
return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
}
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 332744ae6e..3f1155f430 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -38,7 +38,6 @@
#include "core/os/dir_access.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "core/reference.h"
#ifdef TOOLS_ENABLED
@@ -51,13 +50,13 @@
#include "gd_mono_cache.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
-#include "gd_mono_method_thunk.h"
namespace GDMonoUtils {
MonoObject *unmanaged_get_managed(Object *unmanaged) {
- if (!unmanaged)
+ if (!unmanaged) {
return nullptr;
+ }
if (unmanaged->get_script_instance()) {
CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
@@ -90,8 +89,9 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
MonoObject *target = gchandle.get_target();
- if (target)
+ if (target) {
return target;
+ }
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
@@ -196,8 +196,9 @@ GDMonoClass *get_object_class(MonoObject *p_object) {
GDMonoClass *type_get_proxy_class(const StringName &p_type) {
String class_name = p_type;
- if (class_name[0] == '_')
+ 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);
@@ -220,11 +221,14 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
do {
const GDMonoAssembly *assembly = klass->get_assembly();
- if (assembly == GDMono::get_singleton()->get_core_api_assembly())
+
+ if (assembly == GDMono::get_singleton()->get_core_api_assembly()) {
return klass;
+ }
#ifdef TOOLS_ENABLED
- if (assembly == GDMono::get_singleton()->get_editor_api_assembly())
+ if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) {
return klass;
+ }
#endif
} while ((klass = klass->get_parent_class()) != nullptr);
@@ -385,14 +389,6 @@ String get_exception_name_and_message(MonoException *p_exc) {
return res;
}
-void set_exception_message(MonoException *p_exc, String message) {
- MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
- MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = GDMonoMarshal::mono_string_from_godot(message);
- void *params[1] = { msg };
- property_set_value(prop, (MonoObject *)p_exc, params, nullptr);
-}
-
void debug_print_unhandled_exception(MonoException *p_exc) {
print_unhandled_exception(p_exc);
debug_send_unhandled_exception_error(p_exc);
@@ -410,8 +406,9 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
}
static thread_local bool _recursion_flag_ = false;
- if (_recursion_flag_)
+ if (_recursion_flag_) {
return;
+ }
_recursion_flag_ = true;
SCOPE_EXIT { _recursion_flag_ = false; };
@@ -441,8 +438,9 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
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--)
+ 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);
@@ -452,8 +450,9 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
CRASH_COND(inner_exc_prop == nullptr);
MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
- if (inner_exc != nullptr)
+ if (inner_exc != nullptr) {
si.insert(0, separator);
+ }
p_exc = (MonoException *)inner_exc;
}
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index a7ca46f012..5958bf3cc1 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -44,7 +44,8 @@
if (unlikely(m_exc != nullptr)) { \
GDMonoUtils::debug_unhandled_exception(m_exc); \
GD_UNREACHABLE(); \
- }
+ } else \
+ ((void)0)
namespace GDMonoUtils {
@@ -84,10 +85,6 @@ void detach_current_thread(MonoThread *p_mono_thread);
MonoThread *get_current_thread();
bool is_thread_attached();
-_FORCE_INLINE_ bool is_main_thread() {
- return mono_domain_get() != nullptr && mono_thread_get_main() == mono_thread_current();
-}
-
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);
@@ -115,7 +112,6 @@ String get_type_desc(MonoType *p_type);
String get_type_desc(MonoReflectionType *p_reftype);
String get_exception_name_and_message(MonoException *p_exc);
-void set_exception_message(MonoException *p_exc, String message);
void debug_print_unhandled_exception(MonoException *p_exc);
void debug_send_unhandled_exception_error(MonoException *p_exc);
@@ -167,20 +163,24 @@ StringName get_native_godot_class_name(GDMonoClass *p_class);
#define GD_MONO_BEGIN_RUNTIME_INVOKE \
int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
- _runtime_invoke_count_ref += 1;
+ _runtime_invoke_count_ref += 1; \
+ ((void)0)
-#define GD_MONO_END_RUNTIME_INVOKE \
- _runtime_invoke_count_ref -= 1;
+#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)__gdmono__scope__thread__attach__; \
+ ((void)0)
#ifdef DEBUG_ENABLED
-#define GD_MONO_ASSERT_THREAD_ATTACHED \
- { CRASH_COND(!GDMonoUtils::is_thread_attached()); }
+#define GD_MONO_ASSERT_THREAD_ATTACHED \
+ CRASH_COND(!GDMonoUtils::is_thread_attached()); \
+ ((void)0)
#else
-#define GD_MONO_ASSERT_THREAD_ATTACHED
+#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0)
#endif
#endif // GD_MONOUTILS_H
diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp
index 94431e7c30..98c3ba1324 100644
--- a/modules/mono/register_types.cpp
+++ b/modules/mono/register_types.cpp
@@ -62,8 +62,9 @@ void register_mono_types() {
void unregister_mono_types() {
ScriptServer::unregister_language(script_language_cs);
- if (script_language_cs)
+ if (script_language_cs) {
memdelete(script_language_cs);
+ }
ResourceLoader::remove_resource_format_loader(resource_loader_cs);
resource_loader_cs.unref();
@@ -71,6 +72,7 @@ void unregister_mono_types() {
ResourceSaver::remove_resource_format_saver(resource_saver_cs);
resource_saver_cs.unref();
- if (_godotsharp)
+ if (_godotsharp) {
memdelete(_godotsharp);
+ }
}
diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h
index 7fd0d24eb0..e30d9a8abd 100644
--- a/modules/mono/register_types.h
+++ b/modules/mono/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef MONO_REGISTER_TYPES_H
+#define MONO_REGISTER_TYPES_H
+
void register_mono_types();
void unregister_mono_types();
+
+#endif // MONO_REGISTER_TYPES_H
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index ed0dc5cc28..bd67b03c8e 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -51,18 +51,21 @@ bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const Calla
const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
- if (a->target_id != b->target_id)
+ if (a->target_id != b->target_id) {
return false;
+ }
- if (a->signal != b->signal)
+ if (a->signal != b->signal) {
return false;
+ }
return true;
}
bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
- if (compare_equal(p_a, p_b))
+ if (compare_equal(p_a, p_b)) {
return false;
+ }
return p_a < p_b;
}
@@ -145,18 +148,21 @@ bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const Callabl
const EventSignalCallable *a = static_cast<const EventSignalCallable *>(p_a);
const EventSignalCallable *b = static_cast<const EventSignalCallable *>(p_b);
- if (a->owner != b->owner)
+ if (a->owner != b->owner) {
return false;
+ }
- if (a->event_signal != b->event_signal)
+ if (a->event_signal != b->event_signal) {
return false;
+ }
return true;
}
bool EventSignalCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
- if (compare_equal(p_a, p_b))
+ if (compare_equal(p_a, p_b)) {
return false;
+ }
return p_a < p_b;
}
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index dc542477f5..c76619cca4 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -46,7 +46,7 @@
#define GD_UNREACHABLE() \
CRASH_NOW(); \
do { \
- } while (true);
+ } while (true)
#endif
namespace gdmono {
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 1b4fe68582..a619f0b975 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -71,13 +71,12 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
buffer.resize(512);
DWORD dwBufferSize = buffer.size();
- LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
+ 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
- Vector<WCHAR> buffer;
buffer.resize(dwBufferSize);
- res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
+ res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
}
if (res == ERROR_SUCCESS) {
@@ -91,7 +90,7 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
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, p_subkey.c_str(), &hKey);
+ LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
if (res != ERROR_SUCCESS)
goto cleanup;
@@ -128,7 +127,7 @@ LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) {
String default_clr;
HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey);
+ LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
if (res != ERROR_SUCCESS)
goto cleanup;
diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp
index 8e3e51e688..e68466b1cf 100644
--- a/modules/mono/utils/osx_utils.cpp
+++ b/modules/mono/utils/osx_utils.cpp
@@ -38,24 +38,21 @@
#include <CoreServices/CoreServices.h>
bool osx_is_app_bundle_installed(const String &p_bundle_id) {
- CFURLRef app_url = nullptr;
CFStringRef bundle_id = CFStringCreateWithCString(nullptr, p_bundle_id.utf8(), kCFStringEncodingUTF8);
- OSStatus result = LSFindApplicationForInfo(kLSUnknownCreator, bundle_id, nullptr, nullptr, &app_url);
+ CFArrayRef result = LSCopyApplicationURLsForBundleIdentifier(bundle_id, nullptr);
CFRelease(bundle_id);
- if (app_url)
- CFRelease(app_url);
-
- switch (result) {
- case noErr:
+ if (result) {
+ if (CFArrayGetCount(result) > 0) {
+ CFRelease(result);
return true;
- case kLSApplicationNotFoundErr:
- break;
- default:
- break;
+ } else {
+ CFRelease(result);
+ return false;
+ }
+ } else {
+ return false;
}
-
- return false;
}
#endif
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index 973375a471..5d1abd0c09 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -50,52 +50,30 @@
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.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);
- String buffer;
+ Char16String buffer;
buffer.resize((int)expected_size);
- if (::GetCurrentDirectoryW(expected_size, buffer.ptrw()) == 0)
+ if (::GetCurrentDirectoryW(expected_size, (wchar_t *)buffer.ptrw()) == 0)
return ".";
- return buffer.simplify_path();
+ String result;
+ if (result.parse_utf16(buffer.ptr())) {
+ return ".";
+ }
+ return result.simplify_path();
#else
char buffer[PATH_MAX];
- if (::getcwd(buffer, sizeof(buffer)) == nullptr)
+ if (::getcwd(buffer, sizeof(buffer)) == nullptr) {
return ".";
+ }
String result;
- if (result.parse_utf8(buffer))
+ if (result.parse_utf8(buffer)) {
return ".";
+ }
return result.simplify_path();
#endif
@@ -112,7 +90,7 @@ String abspath(const String &p_path) {
String realpath(const String &p_path) {
#ifdef WINDOWS_ENABLED
// Open file without read/write access
- HANDLE hFile = ::CreateFileW(p_path.c_str(), 0,
+ HANDLE hFile = ::CreateFileW((LPCWSTR)(p_path.utf16().get_data()), 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
@@ -126,34 +104,43 @@ String realpath(const String &p_path) {
return p_path;
}
- String buffer;
+ Char16String buffer;
buffer.resize((int)expected_size);
- ::GetFinalPathNameByHandleW(hFile, buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED);
+ ::GetFinalPathNameByHandleW(hFile, (wchar_t *)buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED);
::CloseHandle(hFile);
- return buffer.simplify_path();
+
+ String result;
+ if (result.parse_utf16(buffer.ptr())) {
+ return p_path;
+ }
+
+ return result.simplify_path();
#elif UNIX_ENABLED
char *resolved_path = ::realpath(p_path.utf8().get_data(), nullptr);
- if (!resolved_path)
+ if (!resolved_path) {
return p_path;
+ }
String result;
bool parse_ok = result.parse_utf8(resolved_path);
::free(resolved_path);
- if (parse_ok)
+ if (parse_ok) {
return p_path;
+ }
return result.simplify_path();
#endif
}
String join(const String &p_a, const String &p_b) {
- if (p_a.empty())
+ if (p_a.empty()) {
return p_b;
+ }
- const CharType a_last = p_a[p_a.length() - 1];
+ const char32_t a_last = p_a[p_a.length() - 1];
if ((a_last == '/' || a_last == '\\') ||
(p_b.size() > 0 && (p_b[0] == '/' || p_b[0] == '\\'))) {
return p_a + p_b;
@@ -178,8 +165,9 @@ String relative_to_impl(const String &p_path, const String &p_relative_to) {
} else {
String base_dir = p_relative_to.get_base_dir();
- if (base_dir.length() <= 2 && (base_dir.empty() || base_dir.ends_with(":")))
+ if (base_dir.length() <= 2 && (base_dir.empty() || base_dir.ends_with(":"))) {
return p_path;
+ }
return String("..").plus_file(relative_to_impl(p_path, base_dir));
}
diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h
index 9965f58b0a..bcd8af8bb9 100644
--- a/modules/mono/utils/path_utils.h
+++ b/modules/mono/utils/path_utils.h
@@ -40,8 +40,6 @@ 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);
-String find_executable(const String &p_name);
-
/// Returns a normalized absolute path to the current working directory
String cwd();
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index da1b719d99..65da4328f6 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -38,16 +38,18 @@
namespace {
int sfind(const String &p_text, int p_from) {
- if (p_from < 0)
+ if (p_from < 0) {
return -1;
+ }
int src_len = 2;
int len = p_text.length();
- if (len == 0)
+ if (len == 0) {
return -1;
+ }
- const CharType *src = p_text.c_str();
+ const char32_t *src = p_text.get_data();
for (int i = p_from; i <= (len - src_len); i++) {
bool found = true;
@@ -62,7 +64,7 @@ int sfind(const String &p_text, int p_from) {
found = src[read_pos] == '%';
break;
case 1: {
- CharType c = src[read_pos];
+ char32_t c = src[read_pos];
found = src[read_pos] == 's' || (c >= '0' && c <= '4');
break;
}
@@ -75,8 +77,9 @@ int sfind(const String &p_text, int p_from) {
}
}
- if (found)
+ if (found) {
return i;
+ }
}
return -1;
@@ -85,8 +88,9 @@ 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) {
- if (p_text.length() < 2)
+ if (p_text.length() < 2) {
return p_text;
+ }
Array args;
@@ -117,7 +121,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const
int result = 0;
while ((result = sfind(p_text, search_from)) >= 0) {
- CharType c = p_text[result + 1];
+ char32_t c = p_text[result + 1];
int req_index = (c == 's' ? findex++ : c - '0');
diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h
index b1d7d3fac9..7357e54e35 100644
--- a/modules/opensimplex/noise_texture.h
+++ b/modules/opensimplex/noise_texture.h
@@ -70,7 +70,7 @@ private:
protected:
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
public:
void set_noise(Ref<OpenSimplexNoise> p_noise);
@@ -88,13 +88,13 @@ public:
void set_bump_strength(float p_bump_strength);
float get_bump_strength();
- int get_width() const;
- int get_height() const;
+ int get_width() const override;
+ int get_height() const override;
- virtual RID get_rid() const;
- virtual bool has_alpha() const { return false; }
+ virtual RID get_rid() const override;
+ virtual bool has_alpha() const override { return false; }
- virtual Ref<Image> get_data() const;
+ virtual Ref<Image> get_data() const override;
NoiseTexture();
virtual ~NoiseTexture();
diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp
index 00b3d47db9..b08219d258 100644
--- a/modules/opensimplex/open_simplex_noise.cpp
+++ b/modules/opensimplex/open_simplex_noise.cpp
@@ -110,7 +110,7 @@ Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) {
for (int i = 0; i < p_height; i++) {
for (int j = 0; j < p_width; j++) {
- float v = get_noise_2d(i, j);
+ float v = get_noise_2d(j, i);
v = v * 0.5 + 0.5; // Normalize [0..1]
uint8_t value = uint8_t(CLAMP(v * 255.0, 0, 255));
wd8[(i * p_width + j) * 4 + 0] = value;
diff --git a/modules/opus/SCsub b/modules/opus/SCsub
index e51590d808..52c61fa708 100644
--- a/modules/opus/SCsub
+++ b/modules/opus/SCsub
@@ -227,6 +227,9 @@ if env["builtin_opus"]:
env_opus.Append(CPPDEFINES=["OPUS_ARM_OPT"])
elif "arch" in env and env["arch"] == "arm64":
env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"])
+ elif env["platform"] == "osx":
+ if "arch" in env and env["arch"] == "arm64":
+ env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"])
env_thirdparty = env_opus.Clone()
env_thirdparty.disable_warnings()
diff --git a/modules/opus/config.py b/modules/opus/config.py
index d22f9454ed..9ff7b2dece 100644
--- a/modules/opus/config.py
+++ b/modules/opus/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return env.module_check_dependencies("opus", ["ogg"])
def configure(env):
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 36c0913f62..050dce1aab 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -213,10 +213,12 @@ static void _compress_pvrtc4(Image *p_img) {
int ofs, size, w, h;
img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
Javelin::RgbaBitmap bm(w, h);
+ void *dst = (void *)bm.GetData();
+ copymem(dst, &r[ofs], size);
+ Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
for (int j = 0; j < size / 4; j++) {
- Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
- /* red and Green colors are swapped. */
- new (dp) Javelin::ColorRgba<unsigned char>(r[ofs + 4 * j + 2], r[ofs + 4 * j + 1], r[ofs + 4 * j], r[ofs + 4 * j + 3]);
+ /* red and blue colors are swapped. */
+ SWAP(dp[j].r, dp[j].b);
}
new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm);
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
index 753650adcb..2afacc1d9c 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -7,7 +7,7 @@ env_regex = env_modules.Clone()
if env["builtin_pcre2"]:
thirdparty_dir = "#thirdparty/pcre2/src/"
- thirdparty_flags = ["PCRE2_STATIC", "HAVE_CONFIG_H"]
+ thirdparty_flags = ["PCRE2_STATIC", "HAVE_CONFIG_H", "SUPPORT_UNICODE"]
if env["builtin_pcre2_with_jit"]:
thirdparty_flags.append("SUPPORT_JIT")
diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml
index 3130c53331..312275842a 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -11,7 +11,7 @@
regex.compile("\\w-(\\d+)")
[/codeblock]
The search pattern must be escaped first for GDScript before it is escaped for the expression. For example, [code]compile("\\d+")[/code] would be read by RegEx as [code]\d+[/code]. Similarly, [code]compile("\"(?:\\\\.|[^\"])*\"")[/code] would be read as [code]"(?:\\.|[^"])*"[/code].
- Using [method search] you can find the pattern within the given text. If a pattern is found, [RegExMatch] is returned and you can retrieve details of the results using functions such as [method RegExMatch.get_string] and [method RegExMatch.get_start].
+ Using [method search], you can find the pattern within the given text. If a pattern is found, [RegExMatch] is returned and you can retrieve details of the results using methods such as [method RegExMatch.get_string] and [method RegExMatch.get_start].
[codeblock]
var regex = RegEx.new()
regex.compile("\\w-(\\d+)")
@@ -19,7 +19,7 @@
if result:
print(result.get_string()) # Would print n-0123
[/codeblock]
- The results of capturing groups [code]()[/code] can be retrieved by passing the group number to the various functions in [RegExMatch]. Group 0 is the default and will always refer to the entire pattern. In the above example, calling [code]result.get_string(1)[/code] would give you [code]0123[/code].
+ The results of capturing groups [code]()[/code] can be retrieved by passing the group number to the various methods in [RegExMatch]. Group 0 is the default and will always refer to the entire pattern. In the above example, calling [code]result.get_string(1)[/code] would give you [code]0123[/code].
This version of RegEx also supports named capturing groups, and the names can be used to retrieve the results. If two or more groups have the same name, the name would only refer to the first one with a match.
[codeblock]
var regex = RegEx.new()
@@ -32,8 +32,16 @@
[codeblock]
for result in regex.search_all("d01, d03, d0c, x3f and x42"):
print(result.get_string("digit"))
- # Would print 01 03 3f 42
- # Note that d0c would not match
+ # Would print 01 03 0 3f 42
+ [/codeblock]
+ [b]Example of splitting a string using a RegEx:[/b]
+ [codeblock]
+ var regex = RegEx.new()
+ regex.compile("\\S+") # Negated whitespace character class.
+ var results = []
+ for match in regex.search_all("One Two \n\tThree"):
+ results.push_back(match.get_string())
+ # The `results` array now contains "One", "Two", "Three".
[/codeblock]
[b]Note:[/b] Godot's regex implementation is based on the [url=https://www.pcre.org/]PCRE2[/url] library. You can view the full pattern reference [url=https://www.pcre.org/current/doc/html/pcre2pattern.html]here[/url].
[b]Tip:[/b] You can use [url=https://regexr.com/]Regexr[/url] to test regular expressions online.
diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml
index 151e881b6f..a45de60aef 100644
--- a/modules/regex/doc_classes/RegExMatch.xml
+++ b/modules/regex/doc_classes/RegExMatch.xml
@@ -49,7 +49,7 @@
</methods>
<members>
<member name="names" type="Dictionary" setter="" getter="get_names" default="{}">
- A dictionary of named groups and its corresponding group number. Only groups with that were matched are included. If multiple groups have the same name, that name would refer to the first matching one.
+ A dictionary of named groups and its corresponding group number. Only groups that were matched are included. If multiple groups have the same name, that name would refer to the first matching one.
</member>
<member name="strings" type="Array" setter="" getter="get_strings" default="[ ]">
An [Array] of the match and its capturing groups.
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index 50ca01067b..c10a276eae 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -156,26 +156,13 @@ void RegExMatch::_bind_methods() {
}
void RegEx::_pattern_info(uint32_t what, void *where) const {
- if (sizeof(CharType) == 2) {
- pcre2_pattern_info_16((pcre2_code_16 *)code, what, where);
-
- } else {
- pcre2_pattern_info_32((pcre2_code_32 *)code, what, where);
- }
+ pcre2_pattern_info_32((pcre2_code_32 *)code, what, where);
}
void RegEx::clear() {
- if (sizeof(CharType) == 2) {
- if (code) {
- pcre2_code_free_16((pcre2_code_16 *)code);
- code = nullptr;
- }
-
- } else {
- if (code) {
- pcre2_code_free_32((pcre2_code_32 *)code);
- code = nullptr;
- }
+ if (code) {
+ pcre2_code_free_32((pcre2_code_32 *)code);
+ code = nullptr;
}
}
@@ -187,39 +174,20 @@ Error RegEx::compile(const String &p_pattern) {
PCRE2_SIZE offset;
uint32_t flags = PCRE2_DUPNAMES;
- if (sizeof(CharType) == 2) {
- pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx;
- pcre2_compile_context_16 *cctx = pcre2_compile_context_create_16(gctx);
- PCRE2_SPTR16 p = (PCRE2_SPTR16)pattern.c_str();
-
- code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx);
+ pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
+ pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx);
+ PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.get_data();
- pcre2_compile_context_free_16(cctx);
+ code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx);
- if (!code) {
- PCRE2_UCHAR16 buf[256];
- pcre2_get_error_message_16(err, buf, 256);
- String message = String::num(offset) + ": " + String((const CharType *)buf);
- ERR_PRINT(message.utf8());
- return FAILED;
- }
+ pcre2_compile_context_free_32(cctx);
- } else {
- pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
- pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx);
- PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.c_str();
-
- code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx);
-
- pcre2_compile_context_free_32(cctx);
-
- if (!code) {
- PCRE2_UCHAR32 buf[256];
- pcre2_get_error_message_32(err, buf, 256);
- String message = String::num(offset) + ": " + String((const CharType *)buf);
- ERR_PRINT(message.utf8());
- return FAILED;
- }
+ if (!code) {
+ PCRE2_UCHAR32 buf[256];
+ pcre2_get_error_message_32(err, buf, 256);
+ String message = String::num(offset) + ": " + String((const char32_t *)buf);
+ ERR_PRINT(message.utf8());
+ return FAILED;
}
return OK;
}
@@ -234,69 +202,39 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
length = p_end;
}
- if (sizeof(CharType) == 2) {
- pcre2_code_16 *c = (pcre2_code_16 *)code;
- pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx;
- pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx);
- PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str();
-
- pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx);
-
- int res = pcre2_match_16(c, s, length, p_offset, 0, match, mctx);
+ pcre2_code_32 *c = (pcre2_code_32 *)code;
+ pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
+ pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
+ PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data();
- if (res < 0) {
- pcre2_match_data_free_16(match);
- return nullptr;
- }
-
- uint32_t size = pcre2_get_ovector_count_16(match);
- PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(match);
-
- result->data.resize(size);
-
- for (uint32_t i = 0; i < size; i++) {
- result->data.write[i].start = ovector[i * 2];
- result->data.write[i].end = ovector[i * 2 + 1];
- }
-
- pcre2_match_data_free_16(match);
- pcre2_match_context_free_16(mctx);
-
- } else {
- pcre2_code_32 *c = (pcre2_code_32 *)code;
- pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
- pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
- PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str();
+ pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
- pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
+ int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx);
- int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx);
-
- if (res < 0) {
- pcre2_match_data_free_32(match);
- pcre2_match_context_free_32(mctx);
+ if (res < 0) {
+ pcre2_match_data_free_32(match);
+ pcre2_match_context_free_32(mctx);
- return nullptr;
- }
+ return nullptr;
+ }
- uint32_t size = pcre2_get_ovector_count_32(match);
- PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match);
+ uint32_t size = pcre2_get_ovector_count_32(match);
+ PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match);
- result->data.resize(size);
+ result->data.resize(size);
- for (uint32_t i = 0; i < size; i++) {
- result->data.write[i].start = ovector[i * 2];
- result->data.write[i].end = ovector[i * 2 + 1];
- }
-
- pcre2_match_data_free_32(match);
- pcre2_match_context_free_32(mctx);
+ for (uint32_t i = 0; i < size; i++) {
+ result->data.write[i].start = ovector[i * 2];
+ result->data.write[i].end = ovector[i * 2 + 1];
}
+ pcre2_match_data_free_32(match);
+ pcre2_match_context_free_32(mctx);
+
result->subject = p_subject;
uint32_t count;
- const CharType *table;
+ const char32_t *table;
uint32_t entry_size;
_pattern_info(PCRE2_INFO_NAMECOUNT, &count);
@@ -304,7 +242,7 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
_pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size);
for (uint32_t i = 0; i < count; i++) {
- CharType id = table[i * entry_size];
+ char32_t id = table[i * entry_size];
if (result->data[id].start == -1) {
continue;
}
@@ -344,7 +282,7 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a
const int safety_zone = 1;
PCRE2_SIZE olength = p_subject.length() + 1; // space for output string and one terminating \0 character
- Vector<CharType> output;
+ Vector<char32_t> output;
output.resize(olength + safety_zone);
uint32_t flags = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
@@ -357,55 +295,28 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a
length = p_end;
}
- if (sizeof(CharType) == 2) {
- pcre2_code_16 *c = (pcre2_code_16 *)code;
- pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx;
- pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx);
- PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str();
- PCRE2_SPTR16 r = (PCRE2_SPTR16)p_replacement.c_str();
- PCRE2_UCHAR16 *o = (PCRE2_UCHAR16 *)output.ptrw();
+ pcre2_code_32 *c = (pcre2_code_32 *)code;
+ pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
+ pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
+ PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data();
+ PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.get_data();
+ PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw();
- pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx);
-
- int res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
-
- if (res == PCRE2_ERROR_NOMEMORY) {
- output.resize(olength + safety_zone);
- o = (PCRE2_UCHAR16 *)output.ptrw();
- res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
- }
+ pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
- pcre2_match_data_free_16(match);
- pcre2_match_context_free_16(mctx);
+ int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
- if (res < 0) {
- return String();
- }
-
- } else {
- pcre2_code_32 *c = (pcre2_code_32 *)code;
- pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
- pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
- PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str();
- PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.c_str();
- PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw();
-
- pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
-
- int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
-
- if (res == PCRE2_ERROR_NOMEMORY) {
- output.resize(olength + safety_zone);
- o = (PCRE2_UCHAR32 *)output.ptrw();
- res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
- }
+ if (res == PCRE2_ERROR_NOMEMORY) {
+ output.resize(olength + safety_zone);
+ o = (PCRE2_UCHAR32 *)output.ptrw();
+ res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
+ }
- pcre2_match_data_free_32(match);
- pcre2_match_context_free_32(mctx);
+ pcre2_match_data_free_32(match);
+ pcre2_match_context_free_32(mctx);
- if (res < 0) {
- return String();
- }
+ if (res < 0) {
+ return String();
}
return String(output.ptr(), olength);
@@ -435,7 +346,7 @@ Array RegEx::get_names() const {
ERR_FAIL_COND_V(!is_valid(), result);
uint32_t count;
- const CharType *table;
+ const char32_t *table;
uint32_t entry_size;
_pattern_info(PCRE2_INFO_NAMECOUNT, &count);
@@ -453,39 +364,21 @@ Array RegEx::get_names() const {
}
RegEx::RegEx() {
- if (sizeof(CharType) == 2) {
- general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr);
-
- } else {
- general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
- }
+ general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
code = nullptr;
}
RegEx::RegEx(const String &p_pattern) {
- if (sizeof(CharType) == 2) {
- general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr);
-
- } else {
- general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
- }
+ general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
code = nullptr;
compile(p_pattern);
}
RegEx::~RegEx() {
- if (sizeof(CharType) == 2) {
- if (code) {
- pcre2_code_free_16((pcre2_code_16 *)code);
- }
- pcre2_general_context_free_16((pcre2_general_context_16 *)general_ctx);
-
- } else {
- if (code) {
- pcre2_code_free_32((pcre2_code_32 *)code);
- }
- pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx);
+ if (code) {
+ pcre2_code_free_32((pcre2_code_32 *)code);
}
+ pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx);
}
void RegEx::_bind_methods() {
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index 3aceaf11c5..346833ab9c 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -158,14 +158,16 @@ void AudioStreamOGGVorbis::clear_data() {
void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) {
int src_data_len = p_data.size();
-#define MAX_TEST_MEM (1 << 20)
-
uint32_t alloc_try = 1024;
Vector<char> alloc_mem;
char *w;
stb_vorbis *ogg_stream = nullptr;
stb_vorbis_alloc ogg_alloc;
+ // Vorbis comments may be up to UINT32_MAX, but that's arguably pretty rare.
+ // Let's go with 2^30 so we don't risk going out of bounds.
+ const uint32_t MAX_TEST_MEM = 1 << 30;
+
while (alloc_try < MAX_TEST_MEM) {
alloc_mem.resize(alloc_try);
w = alloc_mem.ptrw();
@@ -205,6 +207,8 @@ void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) {
break;
}
}
+
+ ERR_FAIL_COND_MSG(alloc_try == MAX_TEST_MEM, vformat("Couldn't set vorbis data even with an alloc buffer of %d bytes, report bug.", MAX_TEST_MEM));
}
Vector<uint8_t> AudioStreamOGGVorbis::get_data() const {
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h
index 3002134651..5070f2a078 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h
@@ -52,18 +52,18 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled {
Ref<AudioStreamOGGVorbis> vorbis_stream;
protected:
- virtual void _mix_internal(AudioFrame *p_buffer, int p_frames);
- virtual float get_stream_sampling_rate();
+ virtual void _mix_internal(AudioFrame *p_buffer, int p_frames) override;
+ virtual float get_stream_sampling_rate() override;
public:
- virtual void start(float p_from_pos = 0.0);
- virtual void stop();
- virtual bool is_playing() const;
+ virtual void start(float p_from_pos = 0.0) override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
- virtual int get_loop_count() const; //times it looped
+ virtual int get_loop_count() const override; //times it looped
- virtual float get_playback_position() const;
- virtual void seek(float p_time);
+ virtual float get_playback_position() const override;
+ virtual void seek(float p_time) override;
AudioStreamPlaybackOGGVorbis() {}
~AudioStreamPlaybackOGGVorbis();
@@ -97,13 +97,13 @@ public:
void set_loop_offset(float p_seconds);
float get_loop_offset() const;
- virtual Ref<AudioStreamPlayback> instance_playback();
- virtual String get_stream_name() const;
+ virtual Ref<AudioStreamPlayback> instance_playback() override;
+ virtual String get_stream_name() const override;
void set_data(const Vector<uint8_t> &p_data);
Vector<uint8_t> get_data() const;
- virtual float get_length() const; //if supported, otherwise return 0
+ virtual float get_length() const override; //if supported, otherwise return 0
AudioStreamOGGVorbis();
virtual ~AudioStreamOGGVorbis();
diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.h b/modules/stb_vorbis/resource_importer_ogg_vorbis.h
index 8d6f71a456..47f0039328 100644
--- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.h
@@ -38,19 +38,19 @@ class ResourceImporterOGGVorbis : public ResourceImporter {
GDCLASS(ResourceImporterOGGVorbis, ResourceImporter);
public:
- virtual String get_importer_name() const;
- virtual String get_visible_name() const;
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual String get_save_extension() const;
- virtual String get_resource_type() const;
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
- virtual int get_preset_count() const;
- virtual String get_preset_name(int p_idx) const;
+ virtual int get_preset_count() const override;
+ virtual String get_preset_name(int p_idx) const override;
- virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
- virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr);
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
ResourceImporterOGGVorbis();
};
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index ce889a4928..1475d24792 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -30,6 +30,8 @@
#include "image_loader_tga.h"
+#include "core/error_macros.h"
+#include "core/io/file_access_memory.h"
#include "core/os/os.h"
#include "core/print_string.h"
@@ -311,5 +313,17 @@ void ImageLoaderTGA::get_recognized_extensions(List<String> *p_extensions) const
p_extensions->push_back("tga");
}
+static Ref<Image> _tga_mem_loader_func(const uint8_t *p_png, int p_size) {
+ FileAccessMemory memfile;
+ Error open_memfile_error = memfile.open_custom(p_png, p_size);
+ ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for TGA image buffer.");
+ Ref<Image> img;
+ img.instance();
+ Error load_error = ImageLoaderTGA().load_image(img, &memfile, false, 1.0f);
+ ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load TGA image.");
+ return img;
+}
+
ImageLoaderTGA::ImageLoaderTGA() {
+ Image::_tga_mem_loader_func = _tga_mem_loader_func;
}
diff --git a/modules/theora/config.py b/modules/theora/config.py
index 413acce2df..b063ed51f9 100644
--- a/modules/theora/config.py
+++ b/modules/theora/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return env.module_check_dependencies("theora", ["ogg", "vorbis"])
def configure(env):
diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml
index 92244a4d28..cb8852d5ef 100644
--- a/modules/theora/doc_classes/VideoStreamTheora.xml
+++ b/modules/theora/doc_classes/VideoStreamTheora.xml
@@ -4,7 +4,8 @@
[VideoStream] resource for Ogg Theora videos.
</brief_description>
<description>
- [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension.
+ [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. The Theora codec is less efficient than [VideoStreamWebm]'s VP8 and VP9, but it requires less CPU resources to decode. The Theora codec is decoded on the CPU.
+ [b]Note:[/b] While Ogg Theora videos can also have an [code].ogg[/code] extension, you will have to rename the extension to [code].ogv[/code] to use those videos within Godot.
</description>
<tutorials>
</tutorials>
@@ -22,7 +23,7 @@
<argument index="0" name="file" type="String">
</argument>
<description>
- Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].o[/code] extension.
+ Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension.
</description>
</method>
</methods>
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 4d83e6a4a5..498391e44a 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -581,9 +581,8 @@ bool VideoStreamPlaybackTheora::is_paused() const {
return paused;
};
-void VideoStreamPlaybackTheora::set_loop(bool p_enable){
-
-};
+void VideoStreamPlaybackTheora::set_loop(bool p_enable) {
+}
bool VideoStreamPlaybackTheora::has_loop() const {
return false;
@@ -605,10 +604,8 @@ float VideoStreamPlaybackTheora::get_playback_position() const {
return get_time();
};
-void VideoStreamPlaybackTheora::seek(float p_time){
-
- // no
-};
+void VideoStreamPlaybackTheora::seek(float p_time) {
+}
void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) {
mix_callback = p_callback;
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index f90c2465b4..84f816acf8 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -125,35 +125,35 @@ protected:
void clear();
public:
- virtual void play();
- virtual void stop();
- virtual bool is_playing() const;
+ virtual void play() override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
- virtual void set_paused(bool p_paused);
- virtual bool is_paused() const;
+ virtual void set_paused(bool p_paused) override;
+ virtual bool is_paused() const override;
- virtual void set_loop(bool p_enable);
- virtual bool has_loop() const;
+ virtual void set_loop(bool p_enable) override;
+ virtual bool has_loop() const override;
- virtual float get_length() const;
+ virtual float get_length() const override;
virtual String get_stream_name() const;
virtual int get_loop_count() const;
- virtual float get_playback_position() const;
- virtual void seek(float p_time);
+ virtual float get_playback_position() const override;
+ virtual void seek(float p_time) override;
void set_file(const String &p_file);
- virtual Ref<Texture2D> get_texture() const;
- virtual void update(float p_delta);
+ virtual Ref<Texture2D> get_texture() const override;
+ virtual void update(float p_delta) override;
- virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata);
- virtual int get_channels() const;
- virtual int get_mix_rate() const;
+ virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override;
+ virtual int get_channels() const override;
+ virtual int get_mix_rate() const override;
- virtual void set_audio_track(int p_idx);
+ virtual void set_audio_track(int p_idx) override;
VideoStreamPlaybackTheora();
~VideoStreamPlaybackTheora();
@@ -169,7 +169,7 @@ protected:
static void _bind_methods();
public:
- Ref<VideoStreamPlayback> instance_playback() {
+ Ref<VideoStreamPlayback> instance_playback() override {
Ref<VideoStreamPlaybackTheora> pb = memnew(VideoStreamPlaybackTheora);
pb->set_audio_track(audio_track);
pb->set_file(file);
@@ -178,7 +178,7 @@ public:
void set_file(const String &p_file) { file = p_file; }
String get_file() { return file; }
- void set_audio_track(int p_track) { audio_track = p_track; }
+ void set_audio_track(int p_track) override { audio_track = p_track; }
VideoStreamTheora() { audio_track = 0; }
};
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index 9e7266b95a..5bdcb84244 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -73,8 +73,10 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
}
// Read HALF channel as FLOAT. (GH-13490)
+ bool use_float16 = false;
for (int i = 0; i < exr_header.num_channels; i++) {
if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
+ use_float16 = true;
exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
}
}
@@ -102,33 +104,10 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
idxB = c;
} else if (strcmp(exr_header.channels[c].name, "A") == 0) {
idxA = c;
- }
- }
-
- if (exr_header.num_channels == 1) {
- // Grayscale channel only.
- idxR = 0;
- idxG = 0;
- idxB = 0;
- idxA = 0;
- } else {
- // Assume RGB(A)
- if (idxR == -1) {
- ERR_PRINT("TinyEXR: R channel not found.");
- // @todo { free exr_image }
- return ERR_FILE_CORRUPT;
- }
-
- if (idxG == -1) {
- ERR_PRINT("TinyEXR: G channel not found.");
- // @todo { free exr_image }
- return ERR_FILE_CORRUPT;
- }
-
- if (idxB == -1) {
- ERR_PRINT("TinyEXR: B channel not found.");
- // @todo { free exr_image }
- return ERR_FILE_CORRUPT;
+ } else if (strcmp(exr_header.channels[c].name, "Y") == 0) {
+ idxR = c;
+ idxG = c;
+ idxB = c;
}
}
@@ -138,14 +117,27 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
Image::Format format;
int output_channels = 0;
+ int channel_size = use_float16 ? 2 : 4;
if (idxA != -1) {
- imgdata.resize(exr_image.width * exr_image.height * 8); //RGBA16
- format = Image::FORMAT_RGBAH;
+ imgdata.resize(exr_image.width * exr_image.height * 4 * channel_size); //RGBA
+ format = use_float16 ? Image::FORMAT_RGBAH : Image::FORMAT_RGBAF;
output_channels = 4;
- } else {
- imgdata.resize(exr_image.width * exr_image.height * 6); //RGB16
- format = Image::FORMAT_RGBH;
+ } else if (idxB != -1) {
+ ERR_FAIL_COND_V(idxG == -1, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);
+ imgdata.resize(exr_image.width * exr_image.height * 3 * channel_size); //RGB
+ format = use_float16 ? Image::FORMAT_RGBH : Image::FORMAT_RGBF;
output_channels = 3;
+ } else if (idxG != -1) {
+ ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);
+ imgdata.resize(exr_image.width * exr_image.height * 2 * channel_size); //RG
+ format = use_float16 ? Image::FORMAT_RGH : Image::FORMAT_RGF;
+ output_channels = 2;
+ } else {
+ ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);
+ imgdata.resize(exr_image.width * exr_image.height * 1 * channel_size); //R
+ format = use_float16 ? Image::FORMAT_RH : Image::FORMAT_RF;
+ output_channels = 1;
}
EXRTile single_image_tile;
@@ -175,9 +167,11 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
exr_tiles = exr_image.tiles;
}
+ //print_line("reading format: " + Image::get_format_name(format));
{
uint8_t *wd = imgdata.ptrw();
- uint16_t *iw = (uint16_t *)wd;
+ uint16_t *iw16 = (uint16_t *)wd;
+ float *iw32 = (float *)wd;
// Assume `out_rgba` have enough memory allocated.
for (int tile_index = 0; tile_index < num_tiles; tile_index++) {
@@ -187,41 +181,99 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
int th = tile.height;
const float *r_channel_start = reinterpret_cast<const float *>(tile.images[idxR]);
- const float *g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]);
- const float *b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]);
+ const float *g_channel_start = nullptr;
+ const float *b_channel_start = nullptr;
const float *a_channel_start = nullptr;
+ if (idxG != -1) {
+ g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]);
+ }
+ if (idxB != -1) {
+ b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]);
+ }
if (idxA != -1) {
a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]);
}
- uint16_t *first_row_w = iw + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;
+ uint16_t *first_row_w16 = iw16 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;
+ float *first_row_w32 = iw32 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;
for (int y = 0; y < th; y++) {
const float *r_channel = r_channel_start + y * tile_width;
- const float *g_channel = g_channel_start + y * tile_width;
- const float *b_channel = b_channel_start + y * tile_width;
+ const float *g_channel = nullptr;
+ const float *b_channel = nullptr;
const float *a_channel = nullptr;
-
+ if (g_channel_start) {
+ g_channel = g_channel_start + y * tile_width;
+ }
+ if (b_channel_start) {
+ b_channel = b_channel_start + y * tile_width;
+ }
if (a_channel_start) {
a_channel = a_channel_start + y * tile_width;
}
- uint16_t *row_w = first_row_w + (y * exr_image.width * output_channels);
-
- for (int x = 0; x < tw; x++) {
- Color color(*r_channel++, *g_channel++, *b_channel++);
-
- if (p_force_linear) {
- color = color.to_linear();
+ if (use_float16) {
+ uint16_t *row_w = first_row_w16 + (y * exr_image.width * output_channels);
+
+ for (int x = 0; x < tw; x++) {
+ Color color;
+ color.r = *r_channel++;
+ if (g_channel) {
+ color.g = *g_channel++;
+ }
+ if (b_channel) {
+ color.b = *b_channel++;
+ }
+ if (a_channel) {
+ color.a = *a_channel++;
+ }
+
+ if (p_force_linear) {
+ color = color.to_linear();
+ }
+
+ *row_w++ = Math::make_half_float(color.r);
+ if (g_channel) {
+ *row_w++ = Math::make_half_float(color.g);
+ }
+ if (b_channel) {
+ *row_w++ = Math::make_half_float(color.b);
+ }
+ if (a_channel) {
+ *row_w++ = Math::make_half_float(color.a);
+ }
}
-
- *row_w++ = Math::make_half_float(color.r);
- *row_w++ = Math::make_half_float(color.g);
- *row_w++ = Math::make_half_float(color.b);
-
- if (idxA != -1) {
- *row_w++ = Math::make_half_float(*a_channel++);
+ } else {
+ float *row_w = first_row_w32 + (y * exr_image.width * output_channels);
+
+ for (int x = 0; x < tw; x++) {
+ Color color;
+ color.r = *r_channel++;
+ if (g_channel) {
+ color.g = *g_channel++;
+ }
+ if (b_channel) {
+ color.b = *b_channel++;
+ }
+ if (a_channel) {
+ color.a = *a_channel++;
+ }
+
+ if (p_force_linear) {
+ color = color.to_linear();
+ }
+
+ *row_w++ = color.r;
+ if (g_channel) {
+ *row_w++ = color.g;
+ }
+ if (b_channel) {
+ *row_w++ = color.b;
+ }
+ if (a_channel) {
+ *row_w++ = color.a;
+ }
}
}
}
diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py
index bd459ca344..b15479797c 100644
--- a/modules/visual_script/config.py
+++ b/modules/visual_script/config.py
@@ -8,7 +8,6 @@ def configure(env):
def get_doc_classes():
return [
- "@VisualScript",
"VisualScriptBasicTypeConstant",
"VisualScriptBuiltinFunc",
"VisualScriptClassConstant",
diff --git a/modules/visual_script/doc_classes/@VisualScript.xml b/modules/visual_script/doc_classes/@VisualScript.xml
deleted file mode 100644
index a2b966bfbb..0000000000
--- a/modules/visual_script/doc_classes/@VisualScript.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@VisualScript" version="4.0">
- <brief_description>
- Built-in visual script functions.
- </brief_description>
- <description>
- A list of built-in visual script functions, see [VisualScriptBuiltinFunc] and [VisualScript].
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml
index db1ef2adc6..088d84d2ec 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -9,7 +9,7 @@
You are most likely to use this class via the Visual Script editor or when writing plugins for it.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link>
+ <link title="VisualScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link>
</tutorials>
<methods>
<method name="add_custom_signal">
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index f387c0f288..1af4b46e22 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -36,8 +36,6 @@
#include "scene/main/node.h"
#include "visual_script_nodes.h"
-#include <stdint.h>
-
//used by editor, this is not really saved
void VisualScriptNode::set_breakpoint(bool p_breakpoint) {
breakpoint = p_breakpoint;
@@ -1764,11 +1762,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
}
next = node->sequence_outputs[output];
- if (next) {
- VSDEBUG("GOT NEXT NODE - " + itos(next->get_id()));
- } else {
- VSDEBUG("GOT NEXT NODE - NULL");
- }
+ VSDEBUG("GOT NEXT NODE - " + (next ? itos(next->get_id()) : "NULL"));
}
if (flow_stack) {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index d54b1faf42..cb5ed37ba1 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -249,7 +249,7 @@ private:
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance *> placeholders;
//void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
- virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
void _update_placeholders();
#endif
@@ -264,7 +264,7 @@ protected:
static void _bind_methods();
public:
- bool inherits_script(const Ref<Script> &p_script) const;
+ bool inherits_script(const Ref<Script> &p_script) const override;
// TODO: Remove it in future when breaking changes are acceptable
StringName get_default_func() const;
@@ -330,47 +330,47 @@ public:
void set_instance_base_type(const StringName &p_type);
- virtual bool can_instance() const;
+ virtual bool can_instance() const override;
- virtual Ref<Script> get_base_script() const;
- virtual StringName get_instance_base_type() const;
- virtual ScriptInstance *instance_create(Object *p_this);
- virtual bool instance_has(const Object *p_this) const;
+ virtual Ref<Script> get_base_script() const override;
+ virtual StringName get_instance_base_type() const override;
+ virtual ScriptInstance *instance_create(Object *p_this) override;
+ virtual bool instance_has(const Object *p_this) const override;
- virtual bool has_source_code() const;
- virtual String get_source_code() const;
- virtual void set_source_code(const String &p_code);
- virtual Error reload(bool p_keep_state = false);
+ virtual bool has_source_code() const override;
+ virtual String get_source_code() const override;
+ virtual void set_source_code(const String &p_code) override;
+ virtual Error reload(bool p_keep_state = false) override;
- virtual bool is_tool() const;
- virtual bool is_valid() const;
+ virtual bool is_tool() const override;
+ virtual bool is_valid() const override;
- virtual ScriptLanguage *get_language() const;
+ virtual ScriptLanguage *get_language() const override;
- virtual bool has_script_signal(const StringName &p_signal) const;
- virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+ virtual bool has_script_signal(const StringName &p_signal) const override;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override;
- virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
- virtual void get_script_method_list(List<MethodInfo> *p_list) const;
+ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
- virtual bool has_method(const StringName &p_method) const;
- virtual MethodInfo get_method_info(const StringName &p_method) const;
+ virtual bool has_method(const StringName &p_method) const override;
+ virtual MethodInfo get_method_info(const StringName &p_method) const override;
- virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
- virtual int get_member_line(const StringName &p_member) const;
+ virtual int get_member_line(const StringName &p_member) const override;
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
+ virtual Vector<ScriptNetData> get_rpc_methods() const override;
+ virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
+ virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_property) const;
- virtual StringName get_rset_property(const uint16_t p_rset_property_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual Vector<ScriptNetData> get_rset_properties() const override;
+ virtual uint16_t get_rset_property_id(const StringName &p_property) const override;
+ virtual StringName get_rset_property(const uint16_t p_rset_property_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const override;
+ virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
#ifdef TOOLS_ENABLED
virtual bool are_subnodes_edited() const;
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index a0dcd76d10..177f9192b8 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -1060,7 +1060,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
} break;
case VisualScriptBuiltinFunc::TEXT_CHAR: {
- CharType result[2] = { *p_inputs[0], 0 };
+ char32_t result[2] = { *p_inputs[0], 0 };
*r_return = String(result);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index 1b186353f1..aee2ed79ce 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -122,25 +122,25 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
+ virtual String get_caption() const override;
//virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_category() const override { return "functions"; }
void set_func(BuiltinFunc p_which);
BuiltinFunc get_func();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptBuiltinFunc(VisualScriptBuiltinFunc::BuiltinFunc func);
VisualScriptBuiltinFunc();
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index fea7d151df..b1d8c05d87 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -1124,8 +1124,8 @@ void VisualScriptEditor::_update_members() {
TreeItem *ti = members->create_item(variables);
ti->set_text(0, E->get());
- Variant var = script->get_variable_default_value(E->get());
- ti->set_suffix(0, "= " + String(var));
+
+ ti->set_suffix(0, "= " + _sanitized_variant_text(E->get()));
ti->set_icon(0, type_icons[script->get_variable_info(E->get()).type]);
ti->set_selectable(0, true);
@@ -1167,6 +1167,18 @@ void VisualScriptEditor::_update_members() {
updating_members = false;
}
+String VisualScriptEditor::_sanitized_variant_text(const StringName &property_name) {
+ Variant var = script->get_variable_default_value(property_name);
+
+ if (script->get_variable_info(property_name).type != Variant::NIL) {
+ Callable::CallError ce;
+ const Variant *converted = &var;
+ var = Variant::construct(script->get_variable_info(property_name).type, &converted, 1, ce, false);
+ }
+
+ return String(var);
+}
+
void VisualScriptEditor::_member_selected() {
if (updating_members) {
return;
@@ -2513,6 +2525,8 @@ RES VisualScriptEditor::get_edited_resource() const {
}
void VisualScriptEditor::set_edited_resource(const RES &p_res) {
+ ERR_FAIL_COND(script.is_valid());
+ ERR_FAIL_COND(p_res.is_null());
script = p_res;
signal_editor->script = script;
signal_editor->undo_redo = undo_redo;
@@ -2533,6 +2547,9 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) {
_update_members();
}
+void VisualScriptEditor::enable_editor() {
+}
+
Vector<String> VisualScriptEditor::get_functions() {
return Vector<String>();
}
@@ -2546,6 +2563,9 @@ String VisualScriptEditor::get_name() {
if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
name = script->get_path().get_file();
if (is_unsaved()) {
+ if (script->get_path().empty()) {
+ name = TTR("[unsaved]");
+ }
name += "(*)";
}
} else if (script->get_name() != "") {
@@ -2562,7 +2582,11 @@ Ref<Texture2D> VisualScriptEditor::get_theme_icon() {
}
bool VisualScriptEditor::is_unsaved() {
- return script->is_edited() || script->are_subnodes_edited();
+ bool unsaved =
+ script->is_edited() ||
+ script->are_subnodes_edited() ||
+ script->get_path().empty(); // In memory.
+ return unsaved;
}
Variant VisualScriptEditor::get_edit_state() {
@@ -2670,7 +2694,8 @@ void VisualScriptEditor::reload(bool p_soft) {
_update_graph();
}
-void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
+Array VisualScriptEditor::get_breakpoints() {
+ Array breakpoints;
List<StringName> functions;
script->get_function_list(&functions);
for (List<StringName>::Element *E = functions.front(); E; E = E->next()) {
@@ -2679,10 +2704,11 @@ void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
for (List<int>::Element *F = nodes.front(); F; F = F->next()) {
Ref<VisualScriptNode> vsn = script->get_node(E->get(), F->get());
if (vsn->is_breakpoint()) {
- p_breakpoints->push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
+ breakpoints.push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
}
}
}
+ return breakpoints;
}
void VisualScriptEditor::add_callback(const String &p_function, PackedStringArray p_args) {
@@ -3216,6 +3242,7 @@ void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from,
{
List<VisualScript::DataConnection> data_connections;
script->get_data_connection_list(p_func_from, &data_connections);
+ int func_from_node_id = script->get_function_node_id(p_func_from);
HashMap<int, Map<int, Pair<int, int>>> connections;
@@ -3225,6 +3252,11 @@ void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from,
int out_p = E->get().from_port;
int in_p = E->get().to_port;
+ // skip if the from_node is a function node
+ if (from == func_from_node_id) {
+ continue;
+ }
+
if (!connections.has(to)) {
connections.set(to, Map<int, Pair<int, int>>());
}
@@ -4654,10 +4686,10 @@ void VisualScriptEditor::_member_option(int p_option) {
}
}
-void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+void VisualScriptEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
}
-void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+void VisualScriptEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
}
void VisualScriptEditor::_bind_methods() {
@@ -4680,6 +4712,8 @@ void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members);
ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
+
+ ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &VisualScriptEditor::add_syntax_highlighter);
}
VisualScriptEditor::VisualScriptEditor() {
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 37063b2da1..66e435741f 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -146,6 +146,7 @@ class VisualScriptEditor : public ScriptEditorBase {
bool updating_members;
void _update_members();
+ String _sanitized_variant_text(const StringName &property_name);
StringName selected;
@@ -288,39 +289,40 @@ protected:
static void _bind_methods();
public:
- virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter);
- virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
-
- virtual void apply_code();
- virtual RES get_edited_resource() const;
- virtual void set_edited_resource(const RES &p_res);
- virtual Vector<String> get_functions();
- virtual void reload_text();
- virtual String get_name();
- virtual Ref<Texture2D> get_theme_icon();
- virtual bool is_unsaved();
- virtual Variant get_edit_state();
- virtual void set_edit_state(const Variant &p_state);
- virtual void goto_line(int p_line, bool p_with_error = false);
- virtual void set_executing_line(int p_line);
- virtual void clear_executing_line();
- virtual void trim_trailing_whitespace();
- virtual void insert_final_newline();
- virtual void convert_indent_to_spaces();
- virtual void convert_indent_to_tabs();
- virtual void ensure_focus();
- virtual void tag_saved_version();
- virtual void reload(bool p_soft);
- virtual void get_breakpoints(List<int> *p_breakpoints);
- virtual void add_callback(const String &p_function, PackedStringArray p_args);
- virtual void update_settings();
- virtual bool show_members_overview();
- virtual void set_debugger_active(bool p_active);
- virtual void set_tooltip_request_func(String p_method, Object *p_obj);
- virtual Control *get_edit_menu();
- virtual void clear_edit_menu();
- virtual bool can_lose_focus_on_node_selection() { return false; }
- virtual void validate();
+ virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
+ virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
+
+ virtual void apply_code() override;
+ virtual RES get_edited_resource() const override;
+ virtual void set_edited_resource(const RES &p_res) override;
+ virtual void enable_editor() override;
+ virtual Vector<String> get_functions() override;
+ virtual void reload_text() override;
+ virtual String get_name() override;
+ virtual Ref<Texture2D> get_theme_icon() override;
+ virtual bool is_unsaved() override;
+ virtual Variant get_edit_state() override;
+ virtual void set_edit_state(const Variant &p_state) override;
+ virtual void goto_line(int p_line, bool p_with_error = false) override;
+ virtual void set_executing_line(int p_line) override;
+ virtual void clear_executing_line() override;
+ virtual void trim_trailing_whitespace() override;
+ virtual void insert_final_newline() override;
+ virtual void convert_indent_to_spaces() override;
+ virtual void convert_indent_to_tabs() override;
+ virtual void ensure_focus() override;
+ virtual void tag_saved_version() override;
+ virtual void reload(bool p_soft) override;
+ virtual Array get_breakpoints() override;
+ virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
+ virtual void update_settings() override;
+ virtual bool show_members_overview() override;
+ virtual void set_debugger_active(bool p_active) override;
+ virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
+ virtual Control *get_edit_menu() override;
+ virtual void clear_edit_menu() override;
+ virtual bool can_lose_focus_on_node_selection() override { return false; }
+ virtual void validate() override;
static void register_editor();
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index bd41117497..60a439b291 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -187,7 +187,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
while (true) {
#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++])
- CharType cchar = GET_CHAR();
+ char32_t cchar = GET_CHAR();
if (cchar == 0) {
r_token.type = TK_EOF;
return OK;
@@ -329,7 +329,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
case '"': {
String str;
while (true) {
- CharType ch = GET_CHAR();
+ char32_t ch = GET_CHAR();
if (ch == 0) {
_set_error("Unterminated String");
@@ -340,13 +340,13 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} else if (ch == '\\') {
//escaped characters...
- CharType next = GET_CHAR();
+ char32_t next = GET_CHAR();
if (next == 0) {
_set_error("Unterminated String");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- CharType res = 0;
+ char32_t res = 0;
switch (next) {
case 'b':
@@ -367,7 +367,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
case 'u': {
// hex number
for (int j = 0; j < 4; j++) {
- CharType c = GET_CHAR();
+ char32_t c = GET_CHAR();
if (c == 0) {
_set_error("Unterminated String");
@@ -379,7 +379,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- CharType v;
+ char32_t v;
if (c >= '0' && c <= '9') {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
@@ -431,7 +431,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
#define READING_DONE 4
int reading = READING_INT;
- CharType c = cchar;
+ char32_t c = cchar;
bool exp_sign = false;
bool exp_beg = false;
bool is_float = false;
@@ -489,7 +489,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
r_token.type = TK_CONSTANT;
if (is_float) {
- r_token.value = num.to_double();
+ r_token.value = num.to_float();
} else {
r_token.value = num.to_int();
}
diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h
index dee0213d54..2b3b25842d 100644
--- a/modules/visual_script/visual_script_expression.h
+++ b/modules/visual_script/visual_script_expression.h
@@ -259,22 +259,22 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "operators"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "operators"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptExpression();
~VisualScriptExpression();
diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h
index 45b56b3073..1d0d6d103b 100644
--- a/modules/visual_script/visual_script_flow_control.h
+++ b/modules/visual_script/visual_script_flow_control.h
@@ -43,20 +43,20 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
void set_return_type(Variant::Type);
Variant::Type get_return_type() const;
@@ -64,7 +64,7 @@ public:
void set_enable_return_value(bool p_enable);
bool is_return_value_enabled() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptReturn();
};
@@ -76,22 +76,22 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptCondition();
};
@@ -103,22 +103,22 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptWhile();
};
@@ -130,22 +130,22 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptIterator();
};
@@ -159,25 +159,25 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
void set_steps(int p_steps);
int get_steps() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptSequence();
};
@@ -202,23 +202,23 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
- virtual bool has_mixed_input_and_sequence_ports() const { return true; }
+ virtual String get_output_sequence_port_text(int p_port) const override;
+ virtual bool has_mixed_input_and_sequence_ports() const override { return true; }
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptSwitch();
};
@@ -233,20 +233,20 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
void set_base_type(const StringName &p_type);
StringName get_base_type() const;
@@ -254,9 +254,9 @@ public:
void set_base_script(const String &p_path);
String get_base_script() const;
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptTypeCast();
};
diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h
index 6921f0e820..8372df561f 100644
--- a/modules/visual_script/visual_script_func_nodes.h
+++ b/modules/visual_script/visual_script_func_nodes.h
@@ -75,25 +75,25 @@ private:
Dictionary _get_argument_cache() const;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "functions"; }
void set_basic_type(Variant::Type p_type);
Variant::Type get_basic_type() const;
@@ -125,9 +125,9 @@ public:
void set_rpc_call_mode(RPCCallMode p_mode);
RPCCallMode get_rpc_call_mode() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
VisualScriptFunctionCall();
};
@@ -187,25 +187,25 @@ private:
void _adjust_input_index(PropertyInfo &pinfo) const;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "functions"; }
void set_base_type(const StringName &p_type);
StringName get_base_type() const;
@@ -231,8 +231,8 @@ public:
void set_assign_op(AssignOp p_op);
AssignOp get_assign_op() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
VisualScriptPropertySet();
};
@@ -273,25 +273,25 @@ private:
Variant::Type _get_type_cache() const;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "functions"; }
void set_base_type(const StringName &p_type);
StringName get_base_type() const;
@@ -314,7 +314,7 @@ public:
void set_index(const StringName &p_type);
StringName get_index() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptPropertyGet();
};
@@ -328,30 +328,30 @@ private:
StringName name;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
+ virtual String get_caption() const override;
//virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_category() const override { return "functions"; }
void set_signal(const StringName &p_type);
StringName get_signal() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptEmitSignal();
};
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 87aa64211e..1b77ed3168 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -933,36 +933,36 @@ static const char *op_names[] = {
};
String VisualScriptOperator::get_caption() const {
- static const wchar_t *op_names[] = {
+ static const char32_t *op_names[] = {
//comparison
- L"A = B", //OP_EQUAL,
- L"A \u2260 B", //OP_NOT_EQUAL,
- L"A < B", //OP_LESS,
- L"A \u2264 B", //OP_LESS_EQUAL,
- L"A > B", //OP_GREATER,
- L"A \u2265 B", //OP_GREATER_EQUAL,
+ U"A = B", //OP_EQUAL,
+ U"A \u2260 B", //OP_NOT_EQUAL,
+ U"A < B", //OP_LESS,
+ U"A \u2264 B", //OP_LESS_EQUAL,
+ U"A > B", //OP_GREATER,
+ U"A \u2265 B", //OP_GREATER_EQUAL,
//mathematic
- L"A + B", //OP_ADD,
- L"A - B", //OP_SUBTRACT,
- L"A \u00D7 B", //OP_MULTIPLY,
- L"A \u00F7 B", //OP_DIVIDE,
- L"\u00AC A", //OP_NEGATE,
- L"+ A", //OP_POSITIVE,
- L"A mod B", //OP_MODULE,
- L"A .. B", //OP_STRING_CONCAT,
+ U"A + B", //OP_ADD,
+ U"A - B", //OP_SUBTRACT,
+ U"A \u00D7 B", //OP_MULTIPLY,
+ U"A \u00F7 B", //OP_DIVIDE,
+ U"\u00AC A", //OP_NEGATE,
+ U"+ A", //OP_POSITIVE,
+ U"A mod B", //OP_MODULE,
+ U"A .. B", //OP_STRING_CONCAT,
//bitwise
- L"A << B", //OP_SHIFT_LEFT,
- L"A >> B", //OP_SHIFT_RIGHT,
- L"A & B", //OP_BIT_AND,
- L"A | B", //OP_BIT_OR,
- L"A ^ B", //OP_BIT_XOR,
- L"~A", //OP_BIT_NEGATE,
+ U"A << B", //OP_SHIFT_LEFT,
+ U"A >> B", //OP_SHIFT_RIGHT,
+ U"A & B", //OP_BIT_AND,
+ U"A | B", //OP_BIT_OR,
+ U"A ^ B", //OP_BIT_XOR,
+ U"~A", //OP_BIT_NEGATE,
//logic
- L"A and B", //OP_AND,
- L"A or B", //OP_OR,
- L"A xor B", //OP_XOR,
- L"not A", //OP_NOT,
- L"A in B", //OP_IN,
+ U"A and B", //OP_AND,
+ U"A or B", //OP_OR,
+ U"A xor B", //OP_XOR,
+ U"not A", //OP_NOT,
+ U"A in B", //OP_IN,
};
return op_names[op];
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index fa284b216c..b6061f8838 100644
--- a/modules/visual_script/visual_script_nodes.h
+++ b/modules/visual_script/visual_script_nodes.h
@@ -56,20 +56,20 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "flow_control"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "flow_control"; }
void add_argument(Variant::Type p_type, const String &p_name, int p_index = -1, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(""));
void set_argument_type(int p_argidx, Variant::Type p_type);
@@ -97,7 +97,7 @@ public:
void set_rpc_mode(MultiplayerAPI::RPCMode p_mode);
MultiplayerAPI::RPCMode get_rpc_mode() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptFunction();
};
@@ -142,20 +142,16 @@ public:
virtual bool is_input_port_name_editable() const;
virtual bool is_input_port_type_editable() const;
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
-
- virtual String get_caption() const = 0;
- virtual String get_text() const = 0;
- virtual String get_category() const = 0;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
void add_input_data_port(Variant::Type p_type, const String &p_name, int p_index = -1);
void set_input_data_port_type(int p_idx, Variant::Type p_type);
@@ -177,22 +173,22 @@ class VisualScriptComposeArray : public VisualScriptLists {
GDCLASS(VisualScriptComposeArray, VisualScriptLists)
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "functions"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptComposeArray();
};
@@ -207,19 +203,19 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "operators"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "operators"; }
void set_operator(Variant::Operator p_op);
Variant::Operator get_operator() const;
@@ -227,7 +223,7 @@ public:
void set_typed(Variant::Type p_op);
Variant::Type get_typed() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptOperator();
};
@@ -241,25 +237,25 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "operators"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "operators"; }
void set_typed(Variant::Type p_op);
Variant::Type get_typed() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptSelect();
};
@@ -270,28 +266,28 @@ class VisualScriptVariableGet : public VisualScriptNode {
StringName variable;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
void set_variable(StringName p_variable);
StringName get_variable() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptVariableGet();
};
@@ -302,28 +298,28 @@ class VisualScriptVariableSet : public VisualScriptNode {
StringName variable;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
void set_variable(StringName p_variable);
StringName get_variable() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptVariableSet();
};
@@ -335,23 +331,23 @@ class VisualScriptConstant : public VisualScriptNode {
Variant value;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "constants"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "constants"; }
void set_constant_type(Variant::Type p_type);
Variant::Type get_constant_type() const;
@@ -359,7 +355,7 @@ public:
void set_constant_value(Variant p_value);
Variant get_constant_value() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptConstant();
};
@@ -373,24 +369,24 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
void set_preload(const Ref<Resource> &p_preload);
Ref<Resource> get_preload() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptPreload();
};
@@ -399,21 +395,21 @@ class VisualScriptIndexGet : public VisualScriptNode {
GDCLASS(VisualScriptIndexGet, VisualScriptNode);
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "operators"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "operators"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptIndexGet();
};
@@ -422,21 +418,21 @@ class VisualScriptIndexSet : public VisualScriptNode {
GDCLASS(VisualScriptIndexSet, VisualScriptNode);
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "operators"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "operators"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptIndexSet();
};
@@ -449,24 +445,24 @@ class VisualScriptGlobalConstant : public VisualScriptNode {
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "constants"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "constants"; }
void set_global_constant(int p_which);
int get_global_constant();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptGlobalConstant();
};
@@ -479,22 +475,22 @@ class VisualScriptClassConstant : public VisualScriptNode {
protected:
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "constants"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "constants"; }
void set_class_constant(const StringName &p_which);
StringName get_class_constant();
@@ -502,7 +498,7 @@ public:
void set_base_type(const StringName &p_which);
StringName get_base_type();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptClassConstant();
};
@@ -515,23 +511,23 @@ class VisualScriptBasicTypeConstant : public VisualScriptNode {
protected:
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "constants"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "constants"; }
void set_basic_type_constant(const StringName &p_which);
StringName get_basic_type_constant() const;
@@ -539,7 +535,7 @@ public:
void set_basic_type(Variant::Type p_which);
Variant::Type get_basic_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptBasicTypeConstant();
};
@@ -569,24 +565,24 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "constants"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "constants"; }
void set_math_constant(MathConstant p_which);
MathConstant get_math_constant();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptMathConstant();
};
@@ -599,31 +595,31 @@ class VisualScriptEngineSingleton : public VisualScriptNode {
String singleton;
protected:
- void _validate_property(PropertyInfo &property) const;
+ void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
void set_singleton(const String &p_string);
String get_singleton();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
VisualScriptEngineSingleton();
};
@@ -634,30 +630,30 @@ class VisualScriptSceneNode : public VisualScriptNode {
NodePath path;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
void set_node_path(const NodePath &p_path);
NodePath get_node_path();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
VisualScriptSceneNode();
};
@@ -666,27 +662,27 @@ class VisualScriptSceneTree : public VisualScriptNode {
GDCLASS(VisualScriptSceneTree, VisualScriptNode);
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
VisualScriptSceneTree();
};
@@ -700,24 +696,24 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
void set_resource_path(const String &p_path);
String get_resource_path();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptResourcePath();
};
@@ -729,23 +725,23 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const { return "data"; }
+ virtual String get_caption() const override;
+ virtual String get_category() const override { return "data"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
VisualScriptSelf();
};
@@ -773,22 +769,22 @@ public:
STEP_YIELD_BIT = STEP_SHIFT << 4, //yield (will find VisualScriptFunctionState state in first working memory)
};
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
void _script_changed();
@@ -804,22 +800,22 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptSubCall();
};
@@ -835,20 +831,20 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override;
void set_title(const String &p_title);
String get_title() const;
@@ -859,7 +855,7 @@ public:
void set_size(const Size2 &p_size);
Size2 get_size() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptComment();
};
@@ -874,19 +870,19 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_category() const override;
void set_constructor_type(Variant::Type p_type);
Variant::Type get_constructor_type() const;
@@ -894,7 +890,7 @@ public:
void set_constructor(const Dictionary &p_info);
Dictionary get_constructor() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptConstructor();
};
@@ -909,19 +905,19 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_category() const override;
void set_var_name(const StringName &p_name);
StringName get_var_name() const;
@@ -929,7 +925,7 @@ public:
void set_var_type(Variant::Type p_type);
Variant::Type get_var_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptLocalVar();
};
@@ -944,20 +940,20 @@ protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override;
void set_var_name(const StringName &p_name);
StringName get_var_name() const;
@@ -965,7 +961,7 @@ public:
void set_var_type(Variant::Type p_type);
Variant::Type get_var_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptLocalVarSet();
};
@@ -985,24 +981,24 @@ public:
Mode mode;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_category() const override;
void set_action_name(const StringName &p_name);
StringName get_action_name() const;
@@ -1010,7 +1006,7 @@ public:
void set_action_mode(Mode p_mode);
Mode get_action_mode() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptInputAction();
};
@@ -1033,30 +1029,30 @@ class VisualScriptDeconstruct : public VisualScriptNode {
void _set_elem_cache(const Array &p_elements);
Array _get_elem_cache() const;
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
protected:
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_category() const;
+ virtual String get_caption() const override;
+ virtual String get_category() const override;
void set_deconstruct_type(Variant::Type p_type);
Variant::Type get_deconstruct_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptDeconstruct();
};
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp
index f14c9ce49d..3c44faab90 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -447,7 +447,7 @@ void VisualScriptPropertySelector::_item_selected() {
if (E) {
for (int i = 0; i < E->get().properties.size(); i++) {
if (E->get().properties[i].name == name) {
- text = E->get().properties[i].description;
+ text = DTR(E->get().properties[i].description);
}
}
}
@@ -461,7 +461,7 @@ void VisualScriptPropertySelector::_item_selected() {
if (C) {
for (int i = 0; i < C->get().methods.size(); i++) {
if (C->get().methods[i].name == name) {
- text = C->get().methods[i].description;
+ text = DTR(C->get().methods[i].description);
}
}
}
@@ -473,7 +473,7 @@ void VisualScriptPropertySelector::_item_selected() {
for (int i = 0; i < T->get().methods.size(); i++) {
Vector<String> functions = name.rsplit("/", false, 1);
if (T->get().methods[i].name == functions[functions.size() - 1]) {
- text = T->get().methods[i].description;
+ text = DTR(T->get().methods[i].description);
}
}
}
@@ -492,7 +492,7 @@ void VisualScriptPropertySelector::_item_selected() {
if (typecast_node.is_valid()) {
Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(typecast_node->get_class_name());
if (F) {
- text = F->get().description;
+ text = DTR(F->get().description);
}
}
@@ -502,7 +502,7 @@ void VisualScriptPropertySelector::_item_selected() {
if (F) {
for (int i = 0; i < F->get().constants.size(); i++) {
if (F->get().constants[i].value.to_int() == int(builtin_node->get_func())) {
- text = F->get().constants[i].description;
+ text = DTR(F->get().constants[i].description);
}
}
}
diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h
index 4877e8b7e6..7a72211027 100644
--- a/modules/visual_script/visual_script_yield_nodes.h
+++ b/modules/visual_script/visual_script_yield_nodes.h
@@ -50,25 +50,25 @@ private:
float wait_time;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "functions"; }
void set_yield_mode(YieldMode p_mode);
YieldMode get_yield_mode();
@@ -76,7 +76,7 @@ public:
void set_wait_time(float p_time);
float get_wait_time();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptYield();
};
@@ -103,25 +103,25 @@ private:
StringName _get_base_type() const;
protected:
- virtual void _validate_property(PropertyInfo &property) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
public:
- virtual int get_output_sequence_port_count() const;
- virtual bool has_input_sequence_port() const;
+ virtual int get_output_sequence_port_count() const override;
+ virtual bool has_input_sequence_port() const override;
- virtual String get_output_sequence_port_text(int p_port) const;
+ virtual String get_output_sequence_port_text(int p_port) const override;
- virtual int get_input_value_port_count() const;
- virtual int get_output_value_port_count() const;
+ virtual int get_input_value_port_count() const override;
+ virtual int get_output_value_port_count() const override;
- virtual PropertyInfo get_input_value_port_info(int p_idx) const;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const;
+ virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
+ virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
- virtual String get_caption() const;
- virtual String get_text() const;
- virtual String get_category() const { return "functions"; }
+ virtual String get_caption() const override;
+ virtual String get_text() const override;
+ virtual String get_category() const override { return "functions"; }
void set_base_type(const StringName &p_type);
StringName get_base_type() const;
@@ -135,7 +135,7 @@ public:
void set_call_mode(CallMode p_mode);
CallMode get_call_mode() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
+ virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
VisualScriptYieldSignal();
};
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
index d22f9454ed..8a384e3066 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return env.module_check_dependencies("vorbis", ["ogg"])
def configure(env):
diff --git a/modules/webm/config.py b/modules/webm/config.py
index 93b49d177a..99f8ace114 100644
--- a/modules/webm/config.py
+++ b/modules/webm/config.py
@@ -1,5 +1,8 @@
def can_build(env, platform):
- return platform not in ["iphone"]
+ if platform in ["iphone"]:
+ return False
+
+ return env.module_check_dependencies("webm", ["ogg", "opus", "vorbis"])
def configure(env):
diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml
index dfa04720cf..2edbc08cc8 100644
--- a/modules/webm/doc_classes/VideoStreamWebm.xml
+++ b/modules/webm/doc_classes/VideoStreamWebm.xml
@@ -4,7 +4,8 @@
[VideoStream] resource for WebM videos.
</brief_description>
<description>
- [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension.
+ [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. Both the VP8 and VP9 codecs are supported. The VP8 and VP9 codecs are more efficient than [VideoStreamTheora], but they require more CPU resources to decode (especially VP9). Both the VP8 and VP9 codecs are decoded on the CPU.
+ [b]Note:[/b] There are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora]
</description>
<tutorials>
</tutorials>
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
index dd6866ad0e..d0744fa313 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -238,6 +238,7 @@ else:
is_x11_or_server_arm = (env["platform"] == "linuxbsd" or env["platform"] == "server") and (
platform.machine().startswith("arm") or platform.machine().startswith("aarch")
)
+ is_macos_x86 = env["platform"] == "osx" and ("arch" in env and (env["arch"] != "arm64"))
is_ios_x86 = env["platform"] == "iphone" and ("arch" in env and env["arch"].startswith("x86"))
is_android_x86 = env["platform"] == "android" and env["android_arch"].startswith("x86")
if is_android_x86:
@@ -248,14 +249,15 @@ else:
and (
env["platform"] == "windows"
or env["platform"] == "linuxbsd"
- or env["platform"] == "osx"
or env["platform"] == "haiku"
+ or is_macos_x86
or is_android_x86
or is_ios_x86
)
)
webm_cpu_arm = (
is_x11_or_server_arm
+ or (not is_macos_x86 and env["platform"] == "osx")
or (not is_ios_x86 and env["platform"] == "iphone")
or (not is_android_x86 and env["platform"] == "android")
)
diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h
index f6c148b29b..25675cb248 100644
--- a/modules/webm/video_stream_webm.h
+++ b/modules/webm/video_stream_webm.h
@@ -71,30 +71,30 @@ public:
bool open_file(const String &p_file);
- virtual void stop();
- virtual void play();
+ virtual void stop() override;
+ virtual void play() override;
- virtual bool is_playing() const;
+ virtual bool is_playing() const override;
- virtual void set_paused(bool p_paused);
- virtual bool is_paused() const;
+ virtual void set_paused(bool p_paused) override;
+ virtual bool is_paused() const override;
- virtual void set_loop(bool p_enable);
- virtual bool has_loop() const;
+ virtual void set_loop(bool p_enable) override;
+ virtual bool has_loop() const override;
- virtual float get_length() const;
+ virtual float get_length() const override;
- virtual float get_playback_position() const;
- virtual void seek(float p_time);
+ virtual float get_playback_position() const override;
+ virtual void seek(float p_time) override;
- virtual void set_audio_track(int p_idx);
+ virtual void set_audio_track(int p_idx) override;
- virtual Ref<Texture2D> get_texture() const;
- virtual void update(float p_delta);
+ virtual Ref<Texture2D> get_texture() const override;
+ virtual void update(float p_delta) override;
- virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata);
- virtual int get_channels() const;
- virtual int get_mix_rate() const;
+ virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override;
+ virtual int get_channels() const override;
+ virtual int get_mix_rate() const override;
private:
inline bool has_enough_video_frames() const;
@@ -117,11 +117,11 @@ protected:
public:
VideoStreamWebm();
- virtual Ref<VideoStreamPlayback> instance_playback();
+ virtual Ref<VideoStreamPlayback> instance_playback() override;
virtual void set_file(const String &p_file);
String get_file();
- virtual void set_audio_track(int p_track);
+ virtual void set_audio_track(int p_track) override;
};
class ResourceFormatLoaderWebm : public ResourceFormatLoader {
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 504b4705d8..c80b903e39 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -97,7 +97,7 @@
{
"urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers.
"username": "a_username", # Optional username for the TURN server.
- "credentials": "a_password", # Optional password for the TURN server.
+ "credential": "a_password", # Optional password for the TURN server.
}
]
}
@@ -120,7 +120,7 @@
</argument>
<description>
Sets the SDP description of the local peer. This should be called in response to [signal session_description_created].
- If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created].
+ After calling this function the peer will start emitting [signal ice_candidate_created] (unless an [enum Error] different from [constant OK] is returned).
</description>
</method>
<method name="set_remote_description">
diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h
index e61f786ca1..1407f1e3bd 100644
--- a/modules/webrtc/webrtc_data_channel.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -73,13 +73,6 @@ public:
virtual Error poll() = 0;
virtual void close() = 0;
- /** Inherited from PacketPeer: **/
- virtual int get_available_packet_count() const = 0;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0; ///< buffer is GONE after next get_packet
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0;
-
- virtual int get_max_packet_size() const = 0;
-
WebRTCDataChannel();
~WebRTCDataChannel();
};
diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h
index be3ea13028..03396d207d 100644
--- a/modules/webrtc/webrtc_data_channel_gdnative.h
+++ b/modules/webrtc/webrtc_data_channel_gdnative.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
#ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H
#define WEBRTC_DATA_CHANNEL_GDNATIVE_H
+#ifdef WEBRTC_GDNATIVE_ENABLED
+
#include "modules/gdnative/include/net/godot_net.h"
#include "webrtc_data_channel.h"
@@ -48,33 +48,33 @@ private:
public:
void set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl);
- virtual void set_write_mode(WriteMode mode);
- virtual WriteMode get_write_mode() const;
- virtual bool was_string_packet() const;
+ virtual void set_write_mode(WriteMode mode) override;
+ virtual WriteMode get_write_mode() const override;
+ virtual bool was_string_packet() const override;
- virtual ChannelState get_ready_state() const;
- virtual String get_label() const;
- virtual bool is_ordered() const;
- virtual int get_id() const;
- virtual int get_max_packet_life_time() const;
- virtual int get_max_retransmits() const;
- virtual String get_protocol() const;
- virtual bool is_negotiated() const;
+ virtual ChannelState get_ready_state() const override;
+ virtual String get_label() const override;
+ virtual bool is_ordered() const override;
+ virtual int get_id() const override;
+ virtual int get_max_packet_life_time() const override;
+ virtual int get_max_retransmits() const override;
+ virtual String get_protocol() const override;
+ virtual bool is_negotiated() const override;
- virtual Error poll();
- virtual void close();
+ virtual Error poll() override;
+ virtual void close() override;
/** Inherited from PacketPeer: **/
- virtual int get_available_packet_count() const;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const;
+ virtual int get_max_packet_size() const override;
WebRTCDataChannelGDNative();
~WebRTCDataChannelGDNative();
};
-#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H
-
#endif // WEBRTC_GDNATIVE_ENABLED
+
+#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H
diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h
index 00b5963ea1..7545910e66 100644
--- a/modules/webrtc/webrtc_data_channel_js.h
+++ b/modules/webrtc/webrtc_data_channel_js.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef JAVASCRIPT_ENABLED
-
#ifndef WEBRTC_DATA_CHANNEL_JS_H
#define WEBRTC_DATA_CHANNEL_JS_H
+#ifdef JAVASCRIPT_ENABLED
+
#include "webrtc_data_channel.h"
class WebRTCDataChannelJS : public WebRTCDataChannel {
@@ -60,34 +60,34 @@ public:
void _on_error();
void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string);
- virtual void set_write_mode(WriteMode mode);
- virtual WriteMode get_write_mode() const;
- virtual bool was_string_packet() const;
+ virtual void set_write_mode(WriteMode mode) override;
+ virtual WriteMode get_write_mode() const override;
+ virtual bool was_string_packet() const override;
- virtual ChannelState get_ready_state() const;
- virtual String get_label() const;
- virtual bool is_ordered() const;
- virtual int get_id() const;
- virtual int get_max_packet_life_time() const;
- virtual int get_max_retransmits() const;
- virtual String get_protocol() const;
- virtual bool is_negotiated() const;
+ virtual ChannelState get_ready_state() const override;
+ virtual String get_label() const override;
+ virtual bool is_ordered() const override;
+ virtual int get_id() const override;
+ virtual int get_max_packet_life_time() const override;
+ virtual int get_max_retransmits() const override;
+ virtual String get_protocol() const override;
+ virtual bool is_negotiated() const override;
- virtual Error poll();
- virtual void close();
+ virtual Error poll() override;
+ virtual void close() override;
/** Inherited from PacketPeer: **/
- virtual int get_available_packet_count() const;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const;
+ virtual int get_max_packet_size() const override;
WebRTCDataChannelJS();
WebRTCDataChannelJS(int js_id);
~WebRTCDataChannelJS();
};
-#endif // WEBRTC_DATA_CHANNEL_JS_H
-
#endif // JAVASCRIPT_ENABLED
+
+#endif // WEBRTC_DATA_CHANNEL_JS_H
diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h
index 906b90a1b6..fb37bd7722 100644
--- a/modules/webrtc/webrtc_multiplayer.h
+++ b/modules/webrtc/webrtc_multiplayer.h
@@ -89,27 +89,27 @@ public:
void close();
// PacketPeer
- Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet
- Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- int get_available_packet_count() const;
- int get_max_packet_size() const;
+ Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ int get_available_packet_count() const override;
+ int get_max_packet_size() const override;
// NetworkedMultiplayerPeer
- void set_transfer_mode(TransferMode p_mode);
- TransferMode get_transfer_mode() const;
- void set_target_peer(int p_peer_id);
+ void set_transfer_mode(TransferMode p_mode) override;
+ TransferMode get_transfer_mode() const override;
+ void set_target_peer(int p_peer_id) override;
- int get_unique_id() const;
- int get_packet_peer() const;
+ int get_unique_id() const override;
+ int get_packet_peer() const override;
- bool is_server() const;
+ bool is_server() const override;
- void poll();
+ void poll() override;
- void set_refuse_new_connections(bool p_enable);
- bool is_refusing_new_connections() const;
+ void set_refuse_new_connections(bool p_enable) override;
+ bool is_refusing_new_connections() const override;
- ConnectionStatus get_connection_status() const;
+ ConnectionStatus get_connection_status() const override;
};
-#endif
+#endif // WEBRTC_MULTIPLAYER_H
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h
index 8e59ad62ba..846b65c466 100644
--- a/modules/webrtc/webrtc_peer_connection_gdnative.h
+++ b/modules/webrtc/webrtc_peer_connection_gdnative.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
#ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H
#define WEBRTC_PEER_CONNECTION_GDNATIVE_H
+#ifdef WEBRTC_GDNATIVE_ENABLED
+
#include "modules/gdnative/include/net/godot_net.h"
#include "webrtc_peer_connection.h"
@@ -53,21 +53,21 @@ public:
void set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl);
- virtual ConnectionState get_connection_state() const;
+ virtual ConnectionState get_connection_state() const override;
- virtual Error initialize(Dictionary p_config = Dictionary());
- virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary());
- virtual Error create_offer();
- virtual Error set_remote_description(String type, String sdp);
- virtual Error set_local_description(String type, String sdp);
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName);
- virtual Error poll();
- virtual void close();
+ virtual Error initialize(Dictionary p_config = Dictionary()) override;
+ virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) override;
+ virtual Error create_offer() override;
+ virtual Error set_remote_description(String type, String sdp) override;
+ virtual Error set_local_description(String type, String sdp) override;
+ virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) override;
+ virtual Error poll() override;
+ virtual void close() override;
WebRTCPeerConnectionGDNative();
~WebRTCPeerConnectionGDNative();
};
-#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H
-
#endif // WEBRTC_GDNATIVE_ENABLED
+
+#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H
diff --git a/modules/websocket/editor_debugger_server_websocket.h b/modules/websocket/editor_debugger_server_websocket.h
index c66db1b72a..861f389aab 100644
--- a/modules/websocket/editor_debugger_server_websocket.h
+++ b/modules/websocket/editor_debugger_server_websocket.h
@@ -48,12 +48,12 @@ public:
void _peer_connected(int p_peer, String p_protocol);
void _peer_disconnected(int p_peer, bool p_was_clean);
- void poll();
- Error start();
- void stop();
- bool is_active() const;
- bool is_connection_available() const;
- Ref<RemoteDebuggerPeer> take_connection();
+ void poll() override;
+ Error start() override;
+ void stop() override;
+ bool is_active() const override;
+ bool is_connection_available() const override;
+ Ref<RemoteDebuggerPeer> take_connection() override;
EditorDebuggerServerWebSocket();
~EditorDebuggerServerWebSocket();
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index 1ad249e1eb..bc50de414e 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -42,9 +42,16 @@
#endif
#ifdef TOOLS_ENABLED
#include "editor/debugger/editor_debugger_server.h"
+#include "editor/editor_node.h"
#include "editor_debugger_server_websocket.h"
#endif
+#ifdef TOOLS_ENABLED
+static void _editor_init_callback() {
+ EditorDebuggerServer::register_protocol_handler("ws://", EditorDebuggerServerWebSocket::create);
+}
+#endif
+
void register_websocket_types() {
#ifdef JAVASCRIPT_ENABLED
EMWSPeer::make_default();
@@ -62,7 +69,7 @@ void register_websocket_types() {
ClassDB::register_custom_instance_class<WebSocketPeer>();
#ifdef TOOLS_ENABLED
- EditorDebuggerServer::register_protocol_handler("ws://", EditorDebuggerServerWebSocket::create);
+ EditorNode::add_init_callback(&_editor_init_callback);
#endif
}
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index 3900180739..8feaa9af5a 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -66,7 +66,7 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto
}
// Port
- p_len = host.find_last(":");
+ p_len = host.rfind(":");
if (p_len != -1 && p_len == host.find(":")) {
port = host.substr(p_len, host.length() - p_len).to_int();
host = host.substr(0, p_len);
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
index ba8e21aed6..1053be22e3 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -55,14 +55,12 @@ public:
Ref<X509Certificate> get_trusted_ssl_certificate() const;
void set_trusted_ssl_certificate(Ref<X509Certificate> p_cert);
- virtual void poll() = 0;
virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0;
virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0;
virtual IP_Address get_connected_host() const = 0;
virtual uint16_t get_connected_port() const = 0;
- virtual bool is_server() const;
- virtual ConnectionStatus get_connection_status() const = 0;
+ virtual bool is_server() const override;
void _on_peer_packet();
void _on_connect(String p_protocol);
@@ -70,8 +68,6 @@ public:
void _on_disconnect(bool p_was_clean);
void _on_error();
- virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
-
WebSocketClient();
~WebSocketClient();
};
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index 3ee26e2c5b..af26aef21e 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -79,21 +79,18 @@ protected:
public:
/* NetworkedMultiplayerPeer */
- void set_transfer_mode(TransferMode p_mode);
- TransferMode get_transfer_mode() const;
- void set_target_peer(int p_target_peer);
- int get_packet_peer() const;
- int get_unique_id() const;
- virtual bool is_server() const = 0;
- void set_refuse_new_connections(bool p_enable);
- bool is_refusing_new_connections() const;
- virtual ConnectionStatus get_connection_status() const = 0;
+ void set_transfer_mode(TransferMode p_mode) override;
+ TransferMode get_transfer_mode() const override;
+ void set_target_peer(int p_target_peer) override;
+ int get_packet_peer() const override;
+ int get_unique_id() const override;
+ void set_refuse_new_connections(bool p_enable) override;
+ bool is_refusing_new_connections() const override;
/* PacketPeer */
- virtual int get_available_packet_count() const;
- virtual int get_max_packet_size() const = 0;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
/* WebSocketPeer */
virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index 5f7d1c768b..a61943b615 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -49,11 +49,6 @@ protected:
static void _bind_methods();
public:
- virtual int get_available_packet_count() const = 0;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0;
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0;
- virtual int get_max_packet_size() const = 0;
-
virtual WriteMode get_write_mode() const = 0;
virtual void set_write_mode(WriteMode p_mode) = 0;
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 5df0a37945..064ad4f179 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -50,14 +50,12 @@ protected:
Ref<X509Certificate> ca_chain;
public:
- virtual void poll() = 0;
virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0;
virtual void stop() = 0;
virtual bool is_listening() const = 0;
virtual bool has_peer(int p_id) const = 0;
- virtual Ref<WebSocketPeer> get_peer(int p_id) const = 0;
- virtual bool is_server() const;
- ConnectionStatus get_connection_status() const;
+ virtual bool is_server() const override;
+ ConnectionStatus get_connection_status() const override;
virtual IP_Address get_peer_address(int p_peer_id) const = 0;
virtual int get_peer_port(int p_peer_id) const = 0;
@@ -80,8 +78,6 @@ public:
Ref<X509Certificate> get_ca_chain() const;
void set_ca_chain(Ref<X509Certificate> p_ca_chain);
- virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
-
WebSocketServer();
~WebSocketServer();
};
diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
index b996be2a4c..6242009f67 100644
--- a/modules/xatlas_unwrap/register_types.cpp
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -145,7 +145,7 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Enum::Success, false, xatlas::StringForEnum(err));
printf("Generate..\n");
- xatlas::Generate(atlas, chart_options, nullptr, pack_options);
+ xatlas::Generate(atlas, chart_options, xatlas::ParameterizeOptions(), pack_options);
*r_size_hint_x = atlas->width;
*r_size_hint_y = atlas->height;