godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.67k stars 20.11k forks source link

Crash during script creation when searching for custom base class with template defining inheriting class #76660

Open runefjaer opened 1 year ago

runefjaer commented 1 year ago

Godot version

4.0.2 stable

System information

Ubuntu 22.04.2 LTS

Issue description

I stumbled upon this bug when creating a class "State" and exploring how to set up a template for inheriting scripts, making a script template including the lines

extends State
class_name MyState

as example usage. Then, intending to make a specialized state class script, as I searched the class hierarchy for my original "State" class to be the new script's inherited class, getting only as far as typing "s" (or at most "sta"), the editor crashed. Further testing revealed that the leading "M" of the "MyState" name was the triggering factor; changing that to "YState", because "Y" comes after "S", made the crash go away.

This appears to only occur if the offending script is a script template. (Or more accurately, if the script was a script template when loading the project or when the script was created . Moving it out from the script_templates folder did not stop the editor from crashing, and conversely, having it outside that folder when loading the project, and then moving it inside the script_templates folder, failed to trigger the crash.)

I was unsure whether defining a class inside a script template was allowed, or whether it would simply be ignored by the editor and the class hierarchy, but did not expect a crash. Having class definitions inside script templates be ignored by the rest of the project would be my preference, though resolving the crash and the naming dependent inconsistency is more important.

Steps to reproduce

As a minimal example, a project might contain the following script res://b.gd

extends Node
class_name B

and a script template for the above class, res://script_templates/B/b_template.gd

extends B
class_name AB

(Note that "AB" both lexicographically precedes, and contains the whole or the start of, the name of the base class, "B".)

To trigger the crash, open the dialog for creating a new script, click the button to search the class hierarchy for which class to inherit from, and type "b".

As far as I can tell, the following, which is true in the above example, must be true for the crash to occur:

Minimal reproduction project

This is the above minimal example in the form of a godot project: minimal_example.zip

kleonc commented 1 year ago
================================================================
CrashHandlerException: Program crashed
Engine version: Godot Engine v4.1.dev.custom_build (ee865051367a78bf20f50500520af13ef8f1097b)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[0] CowData<TreeItem::Cell>::_get_size ({godot_path}\core\templates\cowdata.h:81)
[1] CowData<TreeItem::Cell>::size ({godot_path}\core\templates\cowdata.h:131)
[2] Vector<TreeItem::Cell>::size ({godot_path}\core\templates\vector.h:90)
[3] TreeItem::select ({godot_path}\scene\gui\tree.cpp:1043)
[4] CreateDialog::select_type ({godot_path}\editor\create_dialog.cpp:482)
[5] CreateDialog::_update_search ({godot_path}\editor\create_dialog.cpp:197)
[6] CreateDialog::_text_changed ({godot_path}\editor\create_dialog.cpp:420)
[7] call_with_variant_args_helper<CreateDialog,String const &,0> ({godot_path}\core\variant\binder_common.h:293)
[8] call_with_variant_args<CreateDialog,String const &> ({godot_path}\core\variant\binder_common.h:408)
[9] CallableCustomMethodPointer<CreateDialog,String const &>::call ({godot_path}\core\object\callable_method_pointer.h:105)
[10] Callable::callp ({godot_path}\core\variant\callable.cpp:51)
[11] Object::emit_signalp ({godot_path}\core\object\object.cpp:1056)
[12] Object::emit_signal<String> ({godot_path}\core\object\object.h:868)
[13] LineEdit::_emit_text_change ({godot_path}\scene\gui\line_edit.cpp:2255)
[14] LineEdit::_text_changed ({godot_path}\scene\gui\line_edit.cpp:2250)
[15] LineEdit::gui_input ({godot_path}\scene\gui\line_edit.cpp:619)
[16] Control::_call_gui_input ({godot_path}\scene\gui\control.cpp:1752)
[17] Viewport::_gui_input_event ({godot_path}\scene\main\viewport.cpp:2090)
[18] Viewport::push_input ({godot_path}\scene\main\viewport.cpp:2863)
[19] Window::_window_input ({godot_path}\scene\main\window.cpp:1388)
[20] call_with_variant_args_helper<Window,Ref<InputEvent> const &,0> ({godot_path}\core\variant\binder_common.h:293)
[21] call_with_variant_args<Window,Ref<InputEvent> const &> ({godot_path}\core\variant\binder_common.h:408)
[22] CallableCustomMethodPointer<Window,Ref<InputEvent> const &>::call ({godot_path}\core\object\callable_method_pointer.h:105)
[23] Callable::callp ({godot_path}\core\variant\callable.cpp:51)
[24] DisplayServerWindows::_dispatch_input_event ({godot_path}\platform\windows\display_server_windows.cpp:2297)
[25] DisplayServerWindows::_dispatch_input_events ({godot_path}\platform\windows\display_server_windows.cpp:2262)
[26] Input::_parse_input_event_impl ({godot_path}\core\input\input.cpp:690)
[27] Input::flush_buffered_events ({godot_path}\core\input\input.cpp:941)
[28] DisplayServerWindows::process_events ({godot_path}\platform\windows\display_server_windows.cpp:1991)
[29] OS_Windows::run ({godot_path}\platform\windows\os_windows.cpp:1475)
[30] widechar_main ({godot_path}\platform\windows\godot_windows.cpp:181)
[31] _main ({godot_path}\platform\windows\godot_windows.cpp:203)
[32] main ({godot_path}\platform\windows\godot_windows.cpp:217)
[33] WinMain ({godot_path}\platform\windows\godot_windows.cpp:231)
[34] __scrt_common_main_seh (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
[35] <couldn't map PC to fn name>
-- END OF BACKTRACE --
================================================================
Segmentation fault
komugi1211s commented 1 year ago

seems like these lines are the culprit: https://github.com/godotengine/godot/blob/bd1bc68ba07e330e814af19faf87d59da3f0ce6f/editor/create_dialog.cpp#L248-L249

global classes inside of script_templates folder are being added via _add_type. these classes don't return the base script, so it goes through this pass and simply assumes the class it extends to comes from CPP side. this leads to null being registered in search_options_types, and searching for that item tries to read this value, causing null pointer access.

wrapping line 249 with if(ClassDB::class_exists(inherits)) fixes the crash itself, but I'm not sure if template appearing in the "inherit object" dialog is an intended behavior to begin with.