2014-02-10 02:10:30 +01:00
/**************************************************************************/
/* script_language.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "script_language.h"
2018-09-11 18:13:45 +02:00
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2020-02-27 03:30:20 +01:00
# include "core/debugger/engine_debugger.h"
# include "core/debugger/script_debugger.h"
2024-03-07 20:19:05 +01:00
# include "core/io/resource_loader.h"
2020-05-12 17:01:17 +02:00
2020-02-12 11:51:50 +01:00
# include <stdint.h>
2014-02-10 02:10:30 +01:00
ScriptLanguage * ScriptServer : : _languages [ MAX_LANGUAGES ] ;
int ScriptServer : : _language_count = 0 ;
2023-11-09 12:29:47 +01:00
bool ScriptServer : : languages_ready = false ;
Mutex ScriptServer : : languages_mutex ;
2024-09-10 11:29:54 +02:00
thread_local bool ScriptServer : : thread_entered = false ;
2014-02-10 02:10:30 +01:00
bool ScriptServer : : scripting_enabled = true ;
2016-06-13 15:58:32 +02:00
bool ScriptServer : : reload_scripts_on_save = false ;
2020-04-02 01:20:12 +02:00
ScriptEditRequestFunction ScriptServer : : edit_request_func = nullptr ;
2014-02-10 02:10:30 +01:00
void Script : : _notification ( int p_what ) {
2022-02-16 00:52:32 +01:00
switch ( p_what ) {
case NOTIFICATION_POSTINITIALIZE : {
if ( EngineDebugger : : is_active ( ) ) {
2024-04-17 02:01:53 +02:00
callable_mp ( this , & Script : : _set_debugger_break_language ) . call_deferred ( ) ;
2022-02-16 00:52:32 +01:00
}
} break ;
2014-02-10 02:10:30 +01:00
}
}
2019-08-01 21:13:40 +02:00
Variant Script : : _get_property_default_value ( const StringName & p_property ) {
Variant ret ;
get_property_default_value ( p_property , ret ) ;
return ret ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Script : : _get_script_property_list ( ) {
TypedArray < Dictionary > ret ;
2019-08-01 21:13:40 +02:00
List < PropertyInfo > list ;
get_script_property_list ( & list ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : list ) {
2021-07-16 05:45:57 +02:00
ret . append ( E . operator Dictionary ( ) ) ;
2019-08-01 21:13:40 +02:00
}
return ret ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Script : : _get_script_method_list ( ) {
TypedArray < Dictionary > ret ;
2019-08-01 21:13:40 +02:00
List < MethodInfo > list ;
get_script_method_list ( & list ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & E : list ) {
2021-07-16 05:45:57 +02:00
ret . append ( E . operator Dictionary ( ) ) ;
2019-08-01 21:13:40 +02:00
}
return ret ;
}
2022-08-05 20:35:08 +02:00
TypedArray < Dictionary > Script : : _get_script_signal_list ( ) {
TypedArray < Dictionary > ret ;
2019-08-01 21:13:40 +02:00
List < MethodInfo > list ;
get_script_signal_list ( & list ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & E : list ) {
2021-07-16 05:45:57 +02:00
ret . append ( E . operator Dictionary ( ) ) ;
2019-08-01 21:13:40 +02:00
}
return ret ;
}
Dictionary Script : : _get_script_constant_map ( ) {
Dictionary ret ;
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > map ;
2019-08-01 21:13:40 +02:00
get_constants ( & map ) ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Variant > & E : map ) {
ret [ E . key ] = E . value ;
2019-08-01 21:13:40 +02:00
}
return ret ;
}
2024-04-17 02:01:53 +02:00
void Script : : _set_debugger_break_language ( ) {
if ( EngineDebugger : : is_active ( ) ) {
EngineDebugger : : get_script_debugger ( ) - > set_break_language ( get_language ( ) ) ;
}
}
2024-01-28 15:16:09 +01:00
int Script : : get_script_method_argument_count ( const StringName & p_method , bool * r_is_valid ) const {
MethodInfo mi = get_method_info ( p_method ) ;
if ( mi = = MethodInfo ( ) ) {
if ( r_is_valid ) {
* r_is_valid = false ;
}
return 0 ;
}
if ( r_is_valid ) {
* r_is_valid = true ;
}
return mi . arguments . size ( ) ;
}
2022-07-31 10:07:48 +02:00
# ifdef TOOLS_ENABLED
PropertyInfo Script : : get_class_category ( ) const {
String path = get_path ( ) ;
2022-09-29 11:53:28 +02:00
String scr_name ;
2022-07-31 10:07:48 +02:00
if ( is_built_in ( ) ) {
if ( get_name ( ) . is_empty ( ) ) {
2022-09-29 11:53:28 +02:00
scr_name = TTR ( " Built-in script " ) ;
2022-07-31 10:07:48 +02:00
} else {
2022-09-29 11:53:28 +02:00
scr_name = vformat ( " %s (%s) " , get_name ( ) , TTR ( " Built-in " ) ) ;
2022-07-31 10:07:48 +02:00
}
} else {
if ( get_name ( ) . is_empty ( ) ) {
2022-09-29 11:53:28 +02:00
scr_name = path . get_file ( ) ;
2022-07-31 10:07:48 +02:00
} else {
2022-09-29 11:53:28 +02:00
scr_name = get_name ( ) ;
2022-07-31 10:07:48 +02:00
}
}
2022-09-29 11:53:28 +02:00
return PropertyInfo ( Variant : : NIL , scr_name , PROPERTY_HINT_NONE , path , PROPERTY_USAGE_CATEGORY ) ;
2022-07-31 10:07:48 +02:00
}
# endif // TOOLS_ENABLED
2014-02-10 02:10:30 +01:00
void Script : : _bind_methods ( ) {
2021-06-18 00:03:09 +02:00
ClassDB : : bind_method ( D_METHOD ( " can_instantiate " ) , & Script : : can_instantiate ) ;
2017-02-13 12:47:24 +01:00
//ClassDB::bind_method(D_METHOD("instance_create","base_object"),&Script::instance_create);
ClassDB : : bind_method ( D_METHOD ( " instance_has " , " base_object " ) , & Script : : instance_has ) ;
ClassDB : : bind_method ( D_METHOD ( " has_source_code " ) , & Script : : has_source_code ) ;
ClassDB : : bind_method ( D_METHOD ( " get_source_code " ) , & Script : : get_source_code ) ;
ClassDB : : bind_method ( D_METHOD ( " set_source_code " , " source " ) , & Script : : set_source_code ) ;
ClassDB : : bind_method ( D_METHOD ( " reload " , " keep_state " ) , & Script : : reload , DEFVAL ( false ) ) ;
2017-12-19 13:55:55 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_base_script " ) , & Script : : get_base_script ) ;
ClassDB : : bind_method ( D_METHOD ( " get_instance_base_type " ) , & Script : : get_instance_base_type ) ;
2017-08-29 19:33:27 +02:00
2023-12-18 14:56:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_global_name " ) , & Script : : get_global_name ) ;
2017-08-29 19:33:27 +02:00
ClassDB : : bind_method ( D_METHOD ( " has_script_signal " , " signal_name " ) , & Script : : has_script_signal ) ;
2019-08-01 21:13:40 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_script_property_list " ) , & Script : : _get_script_property_list ) ;
ClassDB : : bind_method ( D_METHOD ( " get_script_method_list " ) , & Script : : _get_script_method_list ) ;
ClassDB : : bind_method ( D_METHOD ( " get_script_signal_list " ) , & Script : : _get_script_signal_list ) ;
ClassDB : : bind_method ( D_METHOD ( " get_script_constant_map " ) , & Script : : _get_script_constant_map ) ;
2019-09-24 11:44:48 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_property_default_value " , " property " ) , & Script : : _get_property_default_value ) ;
2019-08-01 21:13:40 +02:00
2017-08-29 19:33:27 +02:00
ClassDB : : bind_method ( D_METHOD ( " is_tool " ) , & Script : : is_tool ) ;
2023-05-23 18:25:34 +02:00
ClassDB : : bind_method ( D_METHOD ( " is_abstract " ) , & Script : : is_abstract ) ;
2018-01-11 23:35:12 +01:00
2024-08-24 07:58:54 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_rpc_config " ) , & Script : : get_rpc_config ) ;
2021-06-18 01:10:18 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " source_code " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NONE ) , " set_source_code " , " get_source_code " ) ;
2014-02-10 02:10:30 +01:00
}
2024-03-07 20:19:05 +01:00
void Script : : reload_from_file ( ) {
# ifdef TOOLS_ENABLED
// Replicates how the ScriptEditor reloads script resources, which generally handles it.
// However, when scripts are to be reloaded but aren't open in the internal editor, we go through here instead.
const Ref < Script > rel = ResourceLoader : : load ( ResourceLoader : : path_remap ( get_path ( ) ) , get_class ( ) , ResourceFormatLoader : : CACHE_MODE_IGNORE ) ;
if ( rel . is_null ( ) ) {
return ;
}
set_source_code ( rel - > get_source_code ( ) ) ;
set_last_modified_time ( rel - > get_last_modified_time ( ) ) ;
reload ( ) ;
# else
Resource : : reload_from_file ( ) ;
# endif
}
2014-02-10 02:10:30 +01:00
void ScriptServer : : set_scripting_enabled ( bool p_enabled ) {
scripting_enabled = p_enabled ;
}
bool ScriptServer : : is_scripting_enabled ( ) {
return scripting_enabled ;
}
ScriptLanguage * ScriptServer : : get_language ( int p_idx ) {
2023-11-09 12:29:47 +01:00
MutexLock lock ( languages_mutex ) ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_INDEX_V ( p_idx , _language_count , nullptr ) ;
2014-02-10 02:10:30 +01:00
return _languages [ p_idx ] ;
}
2024-01-26 13:03:32 +01:00
ScriptLanguage * ScriptServer : : get_language_for_extension ( const String & p_extension ) {
MutexLock lock ( languages_mutex ) ;
for ( int i = 0 ; i < _language_count ; i + + ) {
if ( _languages [ i ] & & _languages [ i ] - > get_extension ( ) = = p_extension ) {
return _languages [ i ] ;
}
}
return nullptr ;
}
2022-10-09 22:03:59 +02:00
Error ScriptServer : : register_language ( ScriptLanguage * p_language ) {
2023-11-09 12:29:47 +01:00
MutexLock lock ( languages_mutex ) ;
2022-10-09 22:03:59 +02:00
ERR_FAIL_NULL_V ( p_language , ERR_INVALID_PARAMETER ) ;
ERR_FAIL_COND_V_MSG ( _language_count > = MAX_LANGUAGES , ERR_UNAVAILABLE , " Script languages limit has been reach, cannot register more. " ) ;
for ( int i = 0 ; i < _language_count ; i + + ) {
const ScriptLanguage * other_language = _languages [ i ] ;
ERR_FAIL_COND_V_MSG ( other_language - > get_extension ( ) = = p_language - > get_extension ( ) , ERR_ALREADY_EXISTS , " A script language with extension ' " + p_language - > get_extension ( ) + " ' is already registered. " ) ;
ERR_FAIL_COND_V_MSG ( other_language - > get_name ( ) = = p_language - > get_name ( ) , ERR_ALREADY_EXISTS , " A script language with name ' " + p_language - > get_name ( ) + " ' is already registered. " ) ;
ERR_FAIL_COND_V_MSG ( other_language - > get_type ( ) = = p_language - > get_type ( ) , ERR_ALREADY_EXISTS , " A script language with type ' " + p_language - > get_type ( ) + " ' is already registered. " ) ;
}
2014-02-10 02:10:30 +01:00
_languages [ _language_count + + ] = p_language ;
2022-10-09 22:03:59 +02:00
return OK ;
2014-02-10 02:10:30 +01:00
}
2022-10-09 22:03:59 +02:00
Error ScriptServer : : unregister_language ( const ScriptLanguage * p_language ) {
2023-11-09 12:29:47 +01:00
MutexLock lock ( languages_mutex ) ;
2016-06-26 15:53:34 +02:00
for ( int i = 0 ; i < _language_count ; i + + ) {
if ( _languages [ i ] = = p_language ) {
_language_count - - ;
if ( i < _language_count ) {
SWAP ( _languages [ i ] , _languages [ _language_count ] ) ;
}
2022-10-09 22:03:59 +02:00
return OK ;
2016-06-26 15:53:34 +02:00
}
}
2022-10-09 22:03:59 +02:00
return ERR_DOES_NOT_EXIST ;
2016-06-26 15:53:34 +02:00
}
2014-11-13 04:53:12 +01:00
void ScriptServer : : init_languages ( ) {
2022-10-18 16:43:37 +02:00
{ // Load global classes.
2018-07-16 00:29:00 +02:00
global_classes_clear ( ) ;
2022-12-25 15:08:32 +01:00
# ifndef DISABLE_DEPRECATED
2018-07-16 00:29:00 +02:00
if ( ProjectSettings : : get_singleton ( ) - > has_setting ( " _global_script_classes " ) ) {
2022-10-18 16:43:37 +02:00
Array script_classes = GLOBAL_GET ( " _global_script_classes " ) ;
2018-07-16 00:29:00 +02:00
2023-12-24 13:44:21 +01:00
for ( const Variant & script_class : script_classes ) {
Dictionary c = script_class ;
2020-08-25 13:01:55 +02:00
if ( ! c . has ( " class " ) | | ! c . has ( " language " ) | | ! c . has ( " path " ) | | ! c . has ( " base " ) ) {
2018-07-16 00:29:00 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-07-16 00:29:00 +02:00
add_global_class ( c [ " class " ] , c [ " base " ] , c [ " language " ] , c [ " path " ] ) ;
}
2022-12-25 15:08:32 +01:00
ProjectSettings : : get_singleton ( ) - > clear ( " _global_script_classes " ) ;
}
# endif
Array script_classes = ProjectSettings : : get_singleton ( ) - > get_global_class_list ( ) ;
2023-12-24 13:44:21 +01:00
for ( const Variant & script_class : script_classes ) {
Dictionary c = script_class ;
2022-12-25 15:08:32 +01:00
if ( ! c . has ( " class " ) | | ! c . has ( " language " ) | | ! c . has ( " path " ) | | ! c . has ( " base " ) ) {
continue ;
}
add_global_class ( c [ " class " ] , c [ " base " ] , c [ " language " ] , c [ " path " ] ) ;
2018-07-16 00:29:00 +02:00
}
}
2023-11-13 16:06:48 +01:00
HashSet < ScriptLanguage * > langs_to_init ;
2023-11-09 12:29:47 +01:00
{
MutexLock lock ( languages_mutex ) ;
for ( int i = 0 ; i < _language_count ; i + + ) {
2023-11-13 16:06:48 +01:00
if ( _languages [ i ] ) {
langs_to_init . insert ( _languages [ i ] ) ;
}
2023-11-09 12:29:47 +01:00
}
2023-11-13 16:06:48 +01:00
}
2023-11-09 12:29:47 +01:00
2023-11-13 16:06:48 +01:00
for ( ScriptLanguage * E : langs_to_init ) {
E - > init ( ) ;
}
{
MutexLock lock ( languages_mutex ) ;
2023-11-09 12:29:47 +01:00
languages_ready = true ;
2014-11-13 04:53:12 +01:00
}
}
2017-07-22 21:11:04 +02:00
void ScriptServer : : finish_languages ( ) {
2023-11-13 16:06:48 +01:00
HashSet < ScriptLanguage * > langs_to_finish ;
2023-11-09 12:29:47 +01:00
2023-11-13 16:06:48 +01:00
{
MutexLock lock ( languages_mutex ) ;
for ( int i = 0 ; i < _language_count ; i + + ) {
if ( _languages [ i ] ) {
langs_to_finish . insert ( _languages [ i ] ) ;
}
}
}
for ( ScriptLanguage * E : langs_to_finish ) {
E - > finish ( ) ;
2017-07-22 21:11:04 +02:00
}
2023-11-09 12:29:47 +01:00
2023-11-13 16:06:48 +01:00
{
MutexLock lock ( languages_mutex ) ;
languages_ready = false ;
}
global_classes_clear ( ) ;
2023-11-09 12:29:47 +01:00
}
bool ScriptServer : : are_languages_initialized ( ) {
MutexLock lock ( languages_mutex ) ;
return languages_ready ;
2017-07-22 21:11:04 +02:00
}
2024-09-10 11:29:54 +02:00
bool ScriptServer : : thread_is_entered ( ) {
return thread_entered ;
}
2016-06-13 15:58:32 +02:00
void ScriptServer : : set_reload_scripts_on_save ( bool p_enable ) {
reload_scripts_on_save = p_enable ;
}
bool ScriptServer : : is_reload_scripts_on_save_enabled ( ) {
return reload_scripts_on_save ;
}
2016-06-25 15:40:33 +02:00
void ScriptServer : : thread_enter ( ) {
2024-09-10 11:29:54 +02:00
if ( thread_entered ) {
return ;
}
2023-11-09 12:29:47 +01:00
MutexLock lock ( languages_mutex ) ;
if ( ! languages_ready ) {
Fix race in ScriptServer.
Fixes #76581.
TSAN flagged this issue on starting the editor:
1. main calls register_core_types, which calls IP::create(), which calls Thread::start on the resolver thread
2. Thread::callback calls ScriptServer::thread_enter(), as "Scripts may need to attach a stack."
3. ScriptServer::thread_enter() accesses ScriptServer::_languages, which is still being initialized on the main thread by initialize_gdscript_module
This fixes the issue by skipping thread enter/exit notifications if languages have not finished initializing yet.
I'm assuming that notifying un-initialized languages of thread starts/stops would have been pointless anyways. If we need to somehow notify languages of threads before the languages initialize, we'll need a different solution.
```
Godot Engine v4.0.2.stable.custom_build.7a0977ce2 - https://godotengine.org
==================
WARNING: ThreadSanitizer: data race (pid=9426)
Write of size 4 at 0x55615b187cd0 by main thread:
#0 ScriptServer::register_language(ScriptLanguage*) /home/rcorre/src/godot/godot/core/object/script_language.cpp:177:28 (godot.linuxbsd.editor.x86_64.llvm.san+0x9e52ab9) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#1 initialize_gdscript_module(ModuleInitializationLevel) /home/rcorre/src/godot/godot/modules/gdscript/register_types.cpp:118:3 (godot.linuxbsd.editor.x86_64.llvm.san+0x36f9c6f) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#2 initialize_modules(ModuleInitializationLevel) /home/rcorre/src/godot/godot/modules/register_module_types.gen.cpp:93:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f50499) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#3 Main::setup2(unsigned long) /home/rcorre/src/godot/godot/main/main.cpp:1961:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f1d40d) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#4 Main::setup(char const*, int, char**, bool) /home/rcorre/src/godot/godot/main/main.cpp:1879:10 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f16370) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#5 main /home/rcorre/src/godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:61:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x2e67e1f) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
Previous read of size 4 at 0x55615b187cd0 by thread T1:
#0 ScriptServer::thread_enter() /home/rcorre/src/godot/godot/core/object/script_language.cpp:244:22 (godot.linuxbsd.editor.x86_64.llvm.san+0x9e54aed) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#1 Thread::callback(unsigned long, Thread::Settings const&, void (*)(void*), void*) /home/rcorre/src/godot/godot/core/os/thread.cpp:61:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x9464ab0) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#2 void std::__invoke_impl<void, void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>(std::__invoke_other, void (*&&)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long&&, Thread::Settings&&, void (*&&)(void*), void*&&) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/invoke.h:61:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#3 std::__invoke_result<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>::type std::__invoke<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>(void (*&&)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long&&, Thread::Settings&&, void (*&&)(void*), void*&&) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/invoke.h:96:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#4 void std::thread::_Invoker<std::tuple<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>>::_M_invoke<0ul, 1ul, 2ul, 3ul, 4ul>(std::_Index_tuple<0ul, 1ul, 2ul, 3ul, 4ul>) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/std_thread.h:258:13 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#5 std::thread::_Invoker<std::tuple<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>>::operator()() /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/std_thread.h:265:11 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#6 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>>>::_M_run() /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/std_thread.h:210:13 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#7 execute_native_thread_routine /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:82:18 (libstdc++.so.6+0xd72c2) (BuildId: 6fe66a2d539a78c993bd2d377e00fad389220963)
Location is global 'ScriptServer::_language_count' of size 4 at 0x55615b187cd0 (godot.linuxbsd.editor.x86_64.llvm.san+0xbf46cd0)
Thread T1 (tid=9431, running) created by main thread at:
#0 pthread_create <null> (godot.linuxbsd.editor.x86_64.llvm.san+0x2de5776) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#1 __gthread_create /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663:35 (libstdc++.so.6+0xd73a9) (BuildId: 6fe66a2d539a78c993bd2d377e00fad389220963)
#2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:147:37 (libstdc++.so.6+0xd73a9)
#3 IP::IP() /home/rcorre/src/godot/godot/core/io/ip.cpp:347:19 (godot.linuxbsd.editor.x86_64.llvm.san+0x962cbcd) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#4 IPUnix::IPUnix() /home/rcorre/src/godot/godot/drivers/unix/ip_unix.cpp:261:9 (godot.linuxbsd.editor.x86_64.llvm.san+0x4aee599) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#5 IPUnix::_create_unix() /home/rcorre/src/godot/godot/drivers/unix/ip_unix.cpp:258:9 (godot.linuxbsd.editor.x86_64.llvm.san+0x4aee599)
#6 IP::create() /home/rcorre/src/godot/godot/core/io/ip.cpp:339:9 (godot.linuxbsd.editor.x86_64.llvm.san+0x962ca5e) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#7 register_core_types() /home/rcorre/src/godot/godot/core/register_core_types.cpp:279:7 (godot.linuxbsd.editor.x86_64.llvm.san+0x93e2333) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#8 Main::setup(char const*, int, char**, bool) /home/rcorre/src/godot/godot/main/main.cpp:690:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f08a49) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#9 main /home/rcorre/src/godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:61:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x2e67e1f) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
SUMMARY: ThreadSanitizer: data race /home/rcorre/src/godot/godot/core/object/script_language.cpp:177:28 in ScriptServer::register_language(ScriptLanguage*)
```
Co-authored-by: Pedro J. Estébanez <RandomShaper@users.noreply.github.com>
2023-04-29 19:54:52 +02:00
return ;
}
2016-06-25 15:40:33 +02:00
for ( int i = 0 ; i < _language_count ; i + + ) {
_languages [ i ] - > thread_enter ( ) ;
}
2024-09-10 11:29:54 +02:00
thread_entered = true ;
2016-06-25 15:40:33 +02:00
}
void ScriptServer : : thread_exit ( ) {
2024-09-10 11:29:54 +02:00
if ( ! thread_entered ) {
return ;
}
2023-11-09 12:29:47 +01:00
MutexLock lock ( languages_mutex ) ;
if ( ! languages_ready ) {
Fix race in ScriptServer.
Fixes #76581.
TSAN flagged this issue on starting the editor:
1. main calls register_core_types, which calls IP::create(), which calls Thread::start on the resolver thread
2. Thread::callback calls ScriptServer::thread_enter(), as "Scripts may need to attach a stack."
3. ScriptServer::thread_enter() accesses ScriptServer::_languages, which is still being initialized on the main thread by initialize_gdscript_module
This fixes the issue by skipping thread enter/exit notifications if languages have not finished initializing yet.
I'm assuming that notifying un-initialized languages of thread starts/stops would have been pointless anyways. If we need to somehow notify languages of threads before the languages initialize, we'll need a different solution.
```
Godot Engine v4.0.2.stable.custom_build.7a0977ce2 - https://godotengine.org
==================
WARNING: ThreadSanitizer: data race (pid=9426)
Write of size 4 at 0x55615b187cd0 by main thread:
#0 ScriptServer::register_language(ScriptLanguage*) /home/rcorre/src/godot/godot/core/object/script_language.cpp:177:28 (godot.linuxbsd.editor.x86_64.llvm.san+0x9e52ab9) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#1 initialize_gdscript_module(ModuleInitializationLevel) /home/rcorre/src/godot/godot/modules/gdscript/register_types.cpp:118:3 (godot.linuxbsd.editor.x86_64.llvm.san+0x36f9c6f) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#2 initialize_modules(ModuleInitializationLevel) /home/rcorre/src/godot/godot/modules/register_module_types.gen.cpp:93:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f50499) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#3 Main::setup2(unsigned long) /home/rcorre/src/godot/godot/main/main.cpp:1961:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f1d40d) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#4 Main::setup(char const*, int, char**, bool) /home/rcorre/src/godot/godot/main/main.cpp:1879:10 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f16370) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#5 main /home/rcorre/src/godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:61:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x2e67e1f) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
Previous read of size 4 at 0x55615b187cd0 by thread T1:
#0 ScriptServer::thread_enter() /home/rcorre/src/godot/godot/core/object/script_language.cpp:244:22 (godot.linuxbsd.editor.x86_64.llvm.san+0x9e54aed) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#1 Thread::callback(unsigned long, Thread::Settings const&, void (*)(void*), void*) /home/rcorre/src/godot/godot/core/os/thread.cpp:61:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x9464ab0) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#2 void std::__invoke_impl<void, void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>(std::__invoke_other, void (*&&)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long&&, Thread::Settings&&, void (*&&)(void*), void*&&) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/invoke.h:61:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#3 std::__invoke_result<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>::type std::__invoke<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>(void (*&&)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long&&, Thread::Settings&&, void (*&&)(void*), void*&&) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/invoke.h:96:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#4 void std::thread::_Invoker<std::tuple<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>>::_M_invoke<0ul, 1ul, 2ul, 3ul, 4ul>(std::_Index_tuple<0ul, 1ul, 2ul, 3ul, 4ul>) /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/std_thread.h:258:13 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#5 std::thread::_Invoker<std::tuple<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>>::operator()() /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/std_thread.h:265:11 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#6 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(unsigned long, Thread::Settings const&, void (*)(void*), void*), unsigned long, Thread::Settings, void (*)(void*), void*>>>::_M_run() /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../include/c++/12.2.1/bits/std_thread.h:210:13 (godot.linuxbsd.editor.x86_64.llvm.san+0x9465283)
#7 execute_native_thread_routine /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:82:18 (libstdc++.so.6+0xd72c2) (BuildId: 6fe66a2d539a78c993bd2d377e00fad389220963)
Location is global 'ScriptServer::_language_count' of size 4 at 0x55615b187cd0 (godot.linuxbsd.editor.x86_64.llvm.san+0xbf46cd0)
Thread T1 (tid=9431, running) created by main thread at:
#0 pthread_create <null> (godot.linuxbsd.editor.x86_64.llvm.san+0x2de5776) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#1 __gthread_create /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663:35 (libstdc++.so.6+0xd73a9) (BuildId: 6fe66a2d539a78c993bd2d377e00fad389220963)
#2 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:147:37 (libstdc++.so.6+0xd73a9)
#3 IP::IP() /home/rcorre/src/godot/godot/core/io/ip.cpp:347:19 (godot.linuxbsd.editor.x86_64.llvm.san+0x962cbcd) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#4 IPUnix::IPUnix() /home/rcorre/src/godot/godot/drivers/unix/ip_unix.cpp:261:9 (godot.linuxbsd.editor.x86_64.llvm.san+0x4aee599) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#5 IPUnix::_create_unix() /home/rcorre/src/godot/godot/drivers/unix/ip_unix.cpp:258:9 (godot.linuxbsd.editor.x86_64.llvm.san+0x4aee599)
#6 IP::create() /home/rcorre/src/godot/godot/core/io/ip.cpp:339:9 (godot.linuxbsd.editor.x86_64.llvm.san+0x962ca5e) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#7 register_core_types() /home/rcorre/src/godot/godot/core/register_core_types.cpp:279:7 (godot.linuxbsd.editor.x86_64.llvm.san+0x93e2333) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#8 Main::setup(char const*, int, char**, bool) /home/rcorre/src/godot/godot/main/main.cpp:690:2 (godot.linuxbsd.editor.x86_64.llvm.san+0x2f08a49) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
#9 main /home/rcorre/src/godot/godot/platform/linuxbsd/godot_linuxbsd.cpp:61:14 (godot.linuxbsd.editor.x86_64.llvm.san+0x2e67e1f) (BuildId: 780a9db7c37d88e78d5ee659c4fa1cd378abd048)
SUMMARY: ThreadSanitizer: data race /home/rcorre/src/godot/godot/core/object/script_language.cpp:177:28 in ScriptServer::register_language(ScriptLanguage*)
```
Co-authored-by: Pedro J. Estébanez <RandomShaper@users.noreply.github.com>
2023-04-29 19:54:52 +02:00
return ;
}
2016-06-25 15:40:33 +02:00
for ( int i = 0 ; i < _language_count ; i + + ) {
_languages [ i ] - > thread_exit ( ) ;
}
2024-09-10 11:29:54 +02:00
thread_entered = false ;
2016-06-25 15:40:33 +02:00
}
2018-07-16 00:29:00 +02:00
HashMap < StringName , ScriptServer : : GlobalScriptClass > ScriptServer : : global_classes ;
2023-01-19 16:47:01 +01:00
HashMap < StringName , Vector < StringName > > ScriptServer : : inheriters_cache ;
bool ScriptServer : : inheriters_cache_dirty = true ;
2018-07-16 00:29:00 +02:00
void ScriptServer : : global_classes_clear ( ) {
global_classes . clear ( ) ;
2023-01-19 16:47:01 +01:00
inheriters_cache . clear ( ) ;
2018-07-16 00:29:00 +02:00
}
void ScriptServer : : add_global_class ( const StringName & p_class , const StringName & p_base , const StringName & p_language , const String & p_path ) {
2019-10-07 12:05:39 +02:00
ERR_FAIL_COND_MSG ( p_class = = p_base | | ( global_classes . has ( p_base ) & & get_global_class_native_base ( p_base ) = = p_class ) , " Cyclic inheritance in script class. " ) ;
2024-03-06 18:14:21 +01:00
GlobalScriptClass * existing = global_classes . getptr ( p_class ) ;
if ( existing ) {
// Update an existing class (only set dirty if something changed).
if ( existing - > base ! = p_base | | existing - > path ! = p_path | | existing - > language ! = p_language ) {
existing - > base = p_base ;
existing - > path = p_path ;
existing - > language = p_language ;
inheriters_cache_dirty = true ;
}
} else {
// Add new class.
GlobalScriptClass g ;
g . language = p_language ;
g . path = p_path ;
g . base = p_base ;
global_classes [ p_class ] = g ;
inheriters_cache_dirty = true ;
}
2018-07-16 00:29:00 +02:00
}
2020-05-14 14:29:06 +02:00
2018-07-16 00:29:00 +02:00
void ScriptServer : : remove_global_class ( const StringName & p_class ) {
global_classes . erase ( p_class ) ;
2023-01-19 16:47:01 +01:00
inheriters_cache_dirty = true ;
}
void ScriptServer : : get_inheriters_list ( const StringName & p_base_type , List < StringName > * r_classes ) {
if ( inheriters_cache_dirty ) {
inheriters_cache . clear ( ) ;
for ( const KeyValue < StringName , GlobalScriptClass > & K : global_classes ) {
if ( ! inheriters_cache . has ( K . value . base ) ) {
inheriters_cache [ K . value . base ] = Vector < StringName > ( ) ;
}
inheriters_cache [ K . value . base ] . push_back ( K . key ) ;
}
for ( KeyValue < StringName , Vector < StringName > > & K : inheriters_cache ) {
K . value . sort_custom < StringName : : AlphCompare > ( ) ;
}
inheriters_cache_dirty = false ;
}
if ( ! inheriters_cache . has ( p_base_type ) ) {
return ;
}
const Vector < StringName > & v = inheriters_cache [ p_base_type ] ;
for ( int i = 0 ; i < v . size ( ) ; i + + ) {
r_classes - > push_back ( v [ i ] ) ;
}
2018-07-16 00:29:00 +02:00
}
2020-05-14 14:29:06 +02:00
2023-01-18 17:32:28 +01:00
void ScriptServer : : remove_global_class_by_path ( const String & p_path ) {
for ( const KeyValue < StringName , GlobalScriptClass > & kv : global_classes ) {
if ( kv . value . path = = p_path ) {
global_classes . erase ( kv . key ) ;
2023-01-19 16:47:01 +01:00
inheriters_cache_dirty = true ;
2023-01-18 17:32:28 +01:00
return ;
}
}
}
2018-07-16 00:29:00 +02:00
bool ScriptServer : : is_global_class ( const StringName & p_class ) {
return global_classes . has ( p_class ) ;
}
2020-05-14 14:29:06 +02:00
2018-07-16 00:29:00 +02:00
StringName ScriptServer : : get_global_class_language ( const StringName & p_class ) {
ERR_FAIL_COND_V ( ! global_classes . has ( p_class ) , StringName ( ) ) ;
return global_classes [ p_class ] . language ;
}
2020-05-14 14:29:06 +02:00
2018-07-16 00:29:00 +02:00
String ScriptServer : : get_global_class_path ( const String & p_class ) {
ERR_FAIL_COND_V ( ! global_classes . has ( p_class ) , String ( ) ) ;
return global_classes [ p_class ] . path ;
}
StringName ScriptServer : : get_global_class_base ( const String & p_class ) {
ERR_FAIL_COND_V ( ! global_classes . has ( p_class ) , String ( ) ) ;
return global_classes [ p_class ] . base ;
}
2020-05-14 14:29:06 +02:00
2019-03-09 04:47:27 +01:00
StringName ScriptServer : : get_global_class_native_base ( const String & p_class ) {
ERR_FAIL_COND_V ( ! global_classes . has ( p_class ) , String ( ) ) ;
String base = global_classes [ p_class ] . base ;
while ( global_classes . has ( base ) ) {
base = global_classes [ base ] . base ;
}
return base ;
}
2020-05-14 14:29:06 +02:00
2018-07-16 00:29:00 +02:00
void ScriptServer : : get_global_class_list ( List < StringName > * r_global_classes ) {
List < StringName > classes ;
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , GlobalScriptClass > & E : global_classes ) {
classes . push_back ( E . key ) ;
2018-07-16 00:29:00 +02:00
}
classes . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & E : classes ) {
2021-07-16 05:45:57 +02:00
r_global_classes - > push_back ( E ) ;
2018-07-16 00:29:00 +02:00
}
}
2020-05-14 14:29:06 +02:00
2018-07-16 00:29:00 +02:00
void ScriptServer : : save_global_classes ( ) {
2022-12-25 15:08:32 +01:00
Dictionary class_icons ;
Array script_classes = ProjectSettings : : get_singleton ( ) - > get_global_class_list ( ) ;
2023-12-24 13:44:21 +01:00
for ( const Variant & script_class : script_classes ) {
Dictionary d = script_class ;
2022-12-25 15:08:32 +01:00
if ( ! d . has ( " name " ) | | ! d . has ( " icon " ) ) {
continue ;
}
class_icons [ d [ " name " ] ] = d [ " icon " ] ;
}
2018-07-16 00:29:00 +02:00
List < StringName > gc ;
get_global_class_list ( & gc ) ;
Array gcarr ;
2021-07-24 15:46:25 +02:00
for ( const StringName & E : gc ) {
2018-07-16 00:29:00 +02:00
Dictionary d ;
2021-07-16 05:45:57 +02:00
d [ " class " ] = E ;
d [ " language " ] = global_classes [ E ] . language ;
d [ " path " ] = global_classes [ E ] . path ;
d [ " base " ] = global_classes [ E ] . base ;
2022-12-25 15:08:32 +01:00
d [ " icon " ] = class_icons . get ( E , " " ) ;
2018-07-16 00:29:00 +02:00
gcarr . push_back ( d ) ;
}
2022-12-25 15:08:32 +01:00
ProjectSettings : : get_singleton ( ) - > store_global_class_list ( gcarr ) ;
2018-07-16 00:29:00 +02:00
}
////////////////////
2022-06-27 22:10:04 +02:00
2020-04-02 01:20:12 +02:00
ScriptCodeCompletionCache * ScriptCodeCompletionCache : : singleton = nullptr ;
2015-06-26 06:14:31 +02:00
ScriptCodeCompletionCache : : ScriptCodeCompletionCache ( ) {
singleton = this ;
}
2020-04-01 21:07:43 +02:00
void ScriptLanguage : : get_core_type_words ( List < String > * p_core_type_words ) const {
p_core_type_words - > push_back ( " String " ) ;
p_core_type_words - > push_back ( " Vector2 " ) ;
p_core_type_words - > push_back ( " Vector2i " ) ;
p_core_type_words - > push_back ( " Rect2 " ) ;
p_core_type_words - > push_back ( " Rect2i " ) ;
p_core_type_words - > push_back ( " Vector3 " ) ;
p_core_type_words - > push_back ( " Vector3i " ) ;
p_core_type_words - > push_back ( " Transform2D " ) ;
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
p_core_type_words - > push_back ( " Vector4 " ) ;
p_core_type_words - > push_back ( " Vector4i " ) ;
2020-04-01 21:07:43 +02:00
p_core_type_words - > push_back ( " Plane " ) ;
2021-06-15 15:04:50 +02:00
p_core_type_words - > push_back ( " Quaternion " ) ;
2020-04-01 21:07:43 +02:00
p_core_type_words - > push_back ( " AABB " ) ;
p_core_type_words - > push_back ( " Basis " ) ;
2021-06-15 15:04:50 +02:00
p_core_type_words - > push_back ( " Transform3D " ) ;
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
p_core_type_words - > push_back ( " Projection " ) ;
2020-04-01 21:07:43 +02:00
p_core_type_words - > push_back ( " Color " ) ;
p_core_type_words - > push_back ( " StringName " ) ;
p_core_type_words - > push_back ( " NodePath " ) ;
p_core_type_words - > push_back ( " RID " ) ;
p_core_type_words - > push_back ( " Callable " ) ;
p_core_type_words - > push_back ( " Signal " ) ;
p_core_type_words - > push_back ( " Dictionary " ) ;
p_core_type_words - > push_back ( " Array " ) ;
p_core_type_words - > push_back ( " PackedByteArray " ) ;
p_core_type_words - > push_back ( " PackedInt32Array " ) ;
p_core_type_words - > push_back ( " PackedInt64Array " ) ;
p_core_type_words - > push_back ( " PackedFloat32Array " ) ;
p_core_type_words - > push_back ( " PackedFloat64Array " ) ;
p_core_type_words - > push_back ( " PackedStringArray " ) ;
p_core_type_words - > push_back ( " PackedVector2Array " ) ;
p_core_type_words - > push_back ( " PackedVector3Array " ) ;
p_core_type_words - > push_back ( " PackedColorArray " ) ;
2024-04-08 16:51:34 +02:00
p_core_type_words - > push_back ( " PackedVector4Array " ) ;
2020-04-01 21:07:43 +02:00
}
2014-02-10 02:10:30 +01:00
void ScriptLanguage : : frame ( ) {
}
2023-05-23 05:12:34 +02:00
TypedArray < int > ScriptLanguage : : CodeCompletionOption : : get_option_characteristics ( const String & p_base ) {
// Return characacteristics of the match found by order of importance.
// Matches will be ranked by a lexicographical order on the vector returned by this function.
// The lower values indicate better matches and that they should go before in the order of appearance.
if ( last_matches = = matches ) {
return charac ;
}
charac . clear ( ) ;
// Ensure base is not empty and at the same time that matches is not empty too.
if ( p_base . length ( ) = = 0 ) {
last_matches = matches ;
charac . push_back ( location ) ;
return charac ;
}
charac . push_back ( matches . size ( ) ) ;
charac . push_back ( ( matches [ 0 ] . first = = 0 ) ? 0 : 1 ) ;
const char32_t * target_char = & p_base [ 0 ] ;
int bad_case = 0 ;
for ( const Pair < int , int > & match_segment : matches ) {
const char32_t * string_to_complete_char = & display [ match_segment . first ] ;
for ( int j = 0 ; j < match_segment . second ; j + + , string_to_complete_char + + , target_char + + ) {
if ( * string_to_complete_char ! = * target_char ) {
bad_case + + ;
}
}
}
charac . push_back ( bad_case ) ;
2023-09-04 17:08:14 +02:00
charac . push_back ( location ) ;
2023-05-23 05:12:34 +02:00
charac . push_back ( matches [ 0 ] . first ) ;
last_matches = matches ;
return charac ;
}
void ScriptLanguage : : CodeCompletionOption : : clear_characteristics ( ) {
charac = TypedArray < int > ( ) ;
}
TypedArray < int > ScriptLanguage : : CodeCompletionOption : : get_option_cached_characteristics ( ) const {
// Only returns the cached value and warns if it was not updated since the last change of matches.
if ( last_matches ! = matches ) {
WARN_PRINT ( " Characteristics are not up to date. " ) ;
}
return charac ;
}
2023-06-11 18:23:48 +02:00
void ScriptLanguage : : _bind_methods ( ) {
BIND_ENUM_CONSTANT ( SCRIPT_NAME_CASING_AUTO ) ;
BIND_ENUM_CONSTANT ( SCRIPT_NAME_CASING_PASCAL_CASE ) ;
BIND_ENUM_CONSTANT ( SCRIPT_NAME_CASING_SNAKE_CASE ) ;
BIND_ENUM_CONSTANT ( SCRIPT_NAME_CASING_KEBAB_CASE ) ;
}
2014-02-10 02:10:30 +01:00
bool PlaceHolderScriptInstance : : set ( const StringName & p_name , const Variant & p_value ) {
2020-05-14 16:41:43 +02:00
if ( script - > is_placeholder_fallback_enabled ( ) ) {
2018-07-29 22:40:09 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
2014-02-10 02:10:30 +01:00
if ( values . has ( p_name ) ) {
2017-08-06 14:32:52 +02:00
Variant defval ;
if ( script - > get_property_default_value ( p_name , defval ) ) {
2022-11-07 05:21:43 +01:00
// The evaluate function ensures that a NIL variant is equal to e.g. an empty Resource.
// Simply doing defval == p_value does not do this.
if ( Variant : : evaluate ( Variant : : OP_EQUAL , defval , p_value ) ) {
2017-08-06 14:32:52 +02:00
values . erase ( p_name ) ;
return true ;
}
}
2014-02-10 02:10:30 +01:00
values [ p_name ] = p_value ;
return true ;
2017-08-06 14:32:52 +02:00
} else {
Variant defval ;
if ( script - > get_property_default_value ( p_name , defval ) ) {
2022-11-07 05:21:43 +01:00
if ( Variant : : evaluate ( Variant : : OP_NOT_EQUAL , defval , p_value ) ) {
2017-08-06 14:32:52 +02:00
values [ p_name ] = p_value ;
}
return true ;
}
2014-02-10 02:10:30 +01:00
}
return false ;
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
bool PlaceHolderScriptInstance : : get ( const StringName & p_name , Variant & r_ret ) const {
if ( values . has ( p_name ) ) {
r_ret = values [ p_name ] ;
return true ;
}
2017-08-06 14:32:52 +02:00
2019-03-05 22:19:02 +01:00
if ( constants . has ( p_name ) ) {
r_ret = constants [ p_name ] ;
return true ;
}
2019-01-10 00:26:00 +01:00
if ( ! script - > is_placeholder_fallback_enabled ( ) ) {
2018-07-29 22:40:09 +02:00
Variant defval ;
if ( script - > get_property_default_value ( p_name , defval ) ) {
r_ret = defval ;
return true ;
}
2017-08-06 14:32:52 +02:00
}
2018-07-29 22:40:09 +02:00
2014-02-10 02:10:30 +01:00
return false ;
}
void PlaceHolderScriptInstance : : get_property_list ( List < PropertyInfo > * p_properties ) const {
2019-01-10 00:26:00 +01:00
if ( script - > is_placeholder_fallback_enabled ( ) ) {
2021-07-16 05:45:57 +02:00
for ( const PropertyInfo & E : properties ) {
p_properties - > push_back ( E ) ;
2018-07-29 22:40:09 +02:00
}
} else {
2021-07-16 05:45:57 +02:00
for ( const PropertyInfo & E : properties ) {
PropertyInfo pinfo = E ;
p_properties - > push_back ( E ) ;
2017-08-06 14:32:52 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
2015-12-05 18:18:22 +01:00
Variant : : Type PlaceHolderScriptInstance : : get_property_type ( const StringName & p_name , bool * r_is_valid ) const {
if ( values . has ( p_name ) ) {
2020-05-14 16:41:43 +02:00
if ( r_is_valid ) {
2015-12-05 18:18:22 +01:00
* r_is_valid = true ;
2020-05-14 16:41:43 +02:00
}
2015-12-05 18:18:22 +01:00
return values [ p_name ] . get_type ( ) ;
}
2019-03-05 22:19:02 +01:00
if ( constants . has ( p_name ) ) {
2020-05-14 16:41:43 +02:00
if ( r_is_valid ) {
2019-03-05 22:19:02 +01:00
* r_is_valid = true ;
2020-05-14 16:41:43 +02:00
}
2019-03-05 22:19:02 +01:00
return constants [ p_name ] . get_type ( ) ;
}
2020-05-14 16:41:43 +02:00
if ( r_is_valid ) {
2015-12-05 18:18:22 +01:00
* r_is_valid = false ;
2020-05-14 16:41:43 +02:00
}
2015-12-05 18:18:22 +01:00
return Variant : : NIL ;
}
2018-07-01 00:58:37 +02:00
void PlaceHolderScriptInstance : : get_method_list ( List < MethodInfo > * p_list ) const {
2020-05-14 16:41:43 +02:00
if ( script - > is_placeholder_fallback_enabled ( ) ) {
2018-07-29 22:40:09 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
2018-07-01 00:58:37 +02:00
if ( script . is_valid ( ) ) {
script - > get_script_method_list ( p_list ) ;
}
}
2020-05-14 14:29:06 +02:00
2018-07-01 00:58:37 +02:00
bool PlaceHolderScriptInstance : : has_method ( const StringName & p_method ) const {
2020-05-14 16:41:43 +02:00
if ( script - > is_placeholder_fallback_enabled ( ) ) {
2018-07-29 22:40:09 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
2018-07-01 00:58:37 +02:00
if ( script . is_valid ( ) ) {
2024-06-22 08:05:46 +02:00
Ref < Script > scr = script ;
while ( scr . is_valid ( ) ) {
if ( scr - > has_method ( p_method ) ) {
return true ;
}
scr = scr - > get_base_script ( ) ;
}
2018-07-01 00:58:37 +02:00
}
return false ;
}
2024-07-18 15:42:54 +02:00
Variant PlaceHolderScriptInstance : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
# if TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return String ( " Attempt to call a method on a placeholder instance. Check if the script is in tool mode. " ) ;
} else {
return String ( " Attempt to call a method on a placeholder instance. Probably a bug, please report. " ) ;
}
2024-08-27 17:26:06 +02:00
# else
2024-07-18 15:42:54 +02:00
return Variant ( ) ;
2024-08-27 17:26:06 +02:00
# endif // TOOLS_ENABLED
2024-07-18 15:42:54 +02:00
}
2022-05-13 15:04:37 +02:00
void PlaceHolderScriptInstance : : update ( const List < PropertyInfo > & p_properties , const HashMap < StringName , Variant > & p_values ) {
2022-05-19 17:00:06 +02:00
HashSet < StringName > new_values ;
2021-07-16 05:45:57 +02:00
for ( const PropertyInfo & E : p_properties ) {
2023-11-28 20:50:10 +01:00
if ( E . usage & ( PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_CATEGORY ) ) {
continue ;
}
2021-07-16 05:45:57 +02:00
StringName n = E . name ;
2014-02-10 02:10:30 +01:00
new_values . insert ( n ) ;
2021-07-16 05:45:57 +02:00
if ( ! values . has ( n ) | | values [ n ] . get_type ( ) ! = E . type ) {
2020-05-14 16:41:43 +02:00
if ( p_values . has ( n ) ) {
2014-02-10 02:10:30 +01:00
values [ n ] = p_values [ n ] ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
properties = p_properties ;
List < StringName > to_remove ;
2022-05-13 15:04:37 +02:00
for ( KeyValue < StringName , Variant > & E : values ) {
if ( ! new_values . has ( E . key ) ) {
to_remove . push_back ( E . key ) ;
2020-05-14 16:41:43 +02:00
}
2017-08-06 14:32:52 +02:00
Variant defval ;
2022-05-13 15:04:37 +02:00
if ( script - > get_property_default_value ( E . key , defval ) ) {
2017-08-06 14:32:52 +02:00
//remove because it's the same as the default value
2022-05-13 15:04:37 +02:00
if ( defval = = E . value ) {
to_remove . push_back ( E . key ) ;
2017-08-06 14:32:52 +02:00
}
}
2014-02-10 02:10:30 +01:00
}
while ( to_remove . size ( ) ) {
values . erase ( to_remove . front ( ) - > get ( ) ) ;
to_remove . pop_front ( ) ;
}
2014-09-22 05:50:48 +02:00
if ( owner & & owner - > get_script_instance ( ) = = this ) {
2021-02-10 21:18:45 +01:00
owner - > notify_property_list_changed ( ) ;
2014-09-22 05:50:48 +02:00
}
2014-02-10 02:10:30 +01:00
//change notify
2019-03-05 22:19:02 +01:00
constants . clear ( ) ;
script - > get_constants ( & constants ) ;
2014-02-10 02:10:30 +01:00
}
2018-07-29 22:40:09 +02:00
void PlaceHolderScriptInstance : : property_set_fallback ( const StringName & p_name , const Variant & p_value , bool * r_valid ) {
2019-01-10 00:26:00 +01:00
if ( script - > is_placeholder_fallback_enabled ( ) ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > : : Iterator E = values . find ( p_name ) ;
2018-07-29 22:40:09 +02:00
if ( E ) {
2022-05-13 15:04:37 +02:00
E - > value = p_value ;
2018-07-29 22:40:09 +02:00
} else {
values . insert ( p_name , p_value ) ;
}
bool found = false ;
2021-07-16 05:45:57 +02:00
for ( const PropertyInfo & F : properties ) {
if ( F . name = = p_name ) {
2018-07-29 22:40:09 +02:00
found = true ;
break ;
}
}
if ( ! found ) {
2024-03-04 22:23:00 +01:00
PropertyHint hint = PROPERTY_HINT_NONE ;
const Object * obj = p_value . get_validated_object ( ) ;
if ( obj & & obj - > is_class ( " Node " ) ) {
hint = PROPERTY_HINT_NODE_TYPE ;
}
properties . push_back ( PropertyInfo ( p_value . get_type ( ) , p_name , hint , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE ) ) ;
2018-07-29 22:40:09 +02:00
}
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2018-07-29 22:40:09 +02:00
* r_valid = false ; // Cannot change the value in either case
2020-05-14 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
}
Variant PlaceHolderScriptInstance : : property_get_fallback ( const StringName & p_name , bool * r_valid ) {
2019-01-10 00:26:00 +01:00
if ( script - > is_placeholder_fallback_enabled ( ) ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > : : ConstIterator E = values . find ( p_name ) ;
2018-07-29 22:40:09 +02:00
if ( E ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2018-07-29 22:40:09 +02:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2022-05-13 15:04:37 +02:00
return E - > value ;
2018-07-29 22:40:09 +02:00
}
2019-03-05 22:19:02 +01:00
E = constants . find ( p_name ) ;
if ( E ) {
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2019-03-05 22:19:02 +01:00
* r_valid = true ;
2020-05-14 16:41:43 +02:00
}
2022-05-13 15:04:37 +02:00
return E - > value ;
2019-03-05 22:19:02 +01:00
}
2018-07-29 22:40:09 +02:00
}
2020-05-14 16:41:43 +02:00
if ( r_valid ) {
2018-07-29 22:40:09 +02:00
* r_valid = false ;
2020-05-14 16:41:43 +02:00
}
2018-07-29 22:40:09 +02:00
return Variant ( ) ;
}
2017-12-06 21:36:34 +01:00
PlaceHolderScriptInstance : : PlaceHolderScriptInstance ( ScriptLanguage * p_language , Ref < Script > p_script , Object * p_owner ) :
owner ( p_owner ) ,
language ( p_language ) ,
2019-01-10 00:26:00 +01:00
script ( p_script ) {
2014-02-10 02:10:30 +01:00
}
PlaceHolderScriptInstance : : ~ PlaceHolderScriptInstance ( ) {
if ( script . is_valid ( ) ) {
script - > _placeholder_erased ( this ) ;
}
}