2017-10-02 23:24:00 +02:00
/**************************************************************************/
/* csharp_script.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
2017-10-02 23:24:00 +02:00
# include "csharp_script.h"
2023-06-13 16:56:21 +02:00
# include "godotsharp_dirs.h"
# include "managed_callable.h"
# include "mono_gd/gd_mono_cache.h"
# include "signal_awaiter_utils.h"
# include "utils/macros.h"
# include "utils/naming_utils.h"
# include "utils/path_utils.h"
# include "utils/string_utils.h"
# ifdef DEBUG_METHODS_ENABLED
# include "class_db_api_json.h"
# endif
# ifdef TOOLS_ENABLED
# include "editor/editor_internal_calls.h"
# include "editor/script_templates/templates.gen.h"
# endif
2017-10-02 23:24:00 +02:00
2020-11-07 19:33:38 -03: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"
2021-06-11 14:51:48 +02:00
# include "core/io/file_access.h"
2020-02-26 11:28:13 +01:00
# include "core/os/mutex.h"
2018-09-11 18:13:45 +02:00
# include "core/os/os.h"
# include "core/os/thread.h"
2023-06-13 16:56:21 +02:00
# include "servers/text_server.h"
2017-10-02 23:24:00 +02:00
# ifdef TOOLS_ENABLED
2021-09-13 14:33:56 +01:00
# include "core/os/keyboard.h"
2023-02-02 00:54:16 +01:00
# include "editor/editor_file_system.h"
2017-10-02 23:24:00 +02:00
# include "editor/editor_node.h"
2021-09-13 14:33:56 +01:00
# include "editor/editor_settings.h"
2022-11-19 12:45:49 +01:00
# include "editor/inspector_dock.h"
2019-12-24 15:17:23 +08:00
# include "editor/node_dock.h"
2017-10-02 23:24:00 +02:00
# endif
2023-06-13 16:56:21 +02:00
# include <stdint.h>
2017-10-02 23:24:00 +02:00
2023-04-12 00:35:10 +02:00
// Types that will be skipped over (in favor of their base types) when setting up instance bindings.
// This must be a superset of `ignored_types` in bindings_generator.cpp.
2023-07-14 16:05:48 +02:00
const Vector < String > ignored_types = { } ;
2023-04-12 00:35:10 +02:00
2017-10-18 08:27:18 +02:00
# ifdef TOOLS_ENABLED
2017-10-05 00:10:51 +02:00
static bool _create_project_solution_if_needed ( ) {
2022-07-28 17:41:51 +02:00
CRASH_COND ( CSharpLanguage : : get_singleton ( ) - > get_godotsharp_editor ( ) = = nullptr ) ;
return CSharpLanguage : : get_singleton ( ) - > get_godotsharp_editor ( ) - > call ( " CreateProjectSolutionIfNeeded " ) ;
2017-10-05 00:10:51 +02:00
}
2017-10-18 08:27:18 +02:00
# endif
2017-10-05 00:10:51 +02:00
2020-03-14 19:20:17 +01:00
CSharpLanguage * CSharpLanguage : : singleton = nullptr ;
2017-10-02 23:24:00 +02:00
2022-12-07 12:11:28 +01:00
GDExtensionInstanceBindingCallbacks CSharpLanguage : : _instance_binding_callbacks = {
2021-08-16 17:16:36 +02:00
& _instance_binding_create_callback ,
& _instance_binding_free_callback ,
& _instance_binding_reference_callback
} ;
2017-10-02 23:24:00 +02:00
String CSharpLanguage : : get_name ( ) const {
return " C# " ;
}
String CSharpLanguage : : get_type ( ) const {
return " CSharpScript " ;
}
String CSharpLanguage : : get_extension ( ) const {
return " cs " ;
}
void CSharpLanguage : : init ( ) {
2023-05-01 22:53:23 +02:00
# ifdef TOOLS_ENABLED
if ( OS : : get_singleton ( ) - > get_cmdline_args ( ) . find ( " --generate-mono-glue " ) ) {
print_verbose ( " .NET: Skipping runtime initialization because glue generation is enabled. " ) ;
return ;
}
# endif
2019-07-09 23:33:29 +02:00
# ifdef DEBUG_METHODS_ENABLED
2020-01-19 20:02:40 +01:00
if ( OS : : get_singleton ( ) - > get_cmdline_args ( ) . find ( " --class-db-json " ) ) {
2019-07-09 23:33:29 +02:00
class_db_api_to_json ( " user://class_db_api.json " , ClassDB : : API_CORE ) ;
# ifdef TOOLS_ENABLED
class_db_api_to_json ( " user://class_db_api_editor.json " , ClassDB : : API_EDITOR ) ;
# endif
}
# endif
2023-01-18 19:43:53 +01:00
GLOBAL_DEF ( " dotnet/project/assembly_name " , " " ) ;
# ifdef TOOLS_ENABLED
GLOBAL_DEF ( " dotnet/project/solution_directory " , " " ) ;
2023-03-31 11:53:16 +02:00
GLOBAL_DEF ( PropertyInfo ( Variant : : INT , " dotnet/project/assembly_reload_attempts " , PROPERTY_HINT_RANGE , " 1,16,1,or_greater " ) , 3 ) ;
2023-01-18 19:43:53 +01:00
# endif
2021-12-28 23:25:16 +01:00
# ifdef TOOLS_ENABLED
2019-07-03 09:44:53 +02:00
EditorNode : : add_init_callback ( & _editor_init_callback ) ;
2017-10-02 23:24:00 +02:00
# endif
2023-10-16 02:55:52 +02:00
gdmono = memnew ( GDMono ) ;
// Initialize only if the project uses C#.
if ( gdmono - > should_initialize ( ) ) {
gdmono - > initialize ( ) ;
}
2017-10-02 23:24:00 +02:00
}
void CSharpLanguage : : finish ( ) {
2020-07-05 19:19:36 +02:00
finalize ( ) ;
}
void CSharpLanguage : : finalize ( ) {
if ( finalized ) {
2020-03-14 19:20:17 +01:00
return ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
2023-06-12 21:59:48 +02:00
if ( gdmono & & gdmono - > is_runtime_initialized ( ) & & GDMonoCache : : godot_api_cache_updated ) {
GDMonoCache : : managed_callbacks . DisposablesTracker_OnGodotShuttingDown ( ) ;
}
2017-12-31 22:37:57 +01:00
finalizing = true ;
2019-02-03 06:35:22 +01:00
// Make sure all script binding gchandles are released before finalizing GDMono
2021-07-26 21:31:17 +02:00
for ( KeyValue < Object * , CSharpScriptBinding > & E : script_bindings ) {
CSharpScriptBinding & script_binding = E . value ;
2019-02-03 06:35:22 +01:00
2019-12-11 17:08:40 +01:00
if ( ! script_binding . gchandle . is_released ( ) ) {
script_binding . gchandle . release ( ) ;
2019-02-03 06:35:22 +01:00
script_binding . inited = false ;
}
}
2017-11-01 01:48:47 +01:00
2017-10-02 23:24:00 +02:00
if ( gdmono ) {
memdelete ( gdmono ) ;
2020-04-02 01:20:12 +02:00
gdmono = nullptr ;
2017-10-02 23:24:00 +02:00
}
2017-12-31 22:37:57 +01:00
2019-02-03 06:35:22 +01:00
// Clear here, after finalizing all domains to make sure there is nothing else referencing the elements.
script_bindings . clear ( ) ;
2020-01-13 21:00:07 +01:00
# ifdef DEBUG_ENABLED
2021-07-26 21:31:17 +02:00
for ( const KeyValue < ObjectID , int > & E : unsafe_object_references ) {
const ObjectID & id = E . key ;
2020-01-13 21:00:07 +01:00
Object * obj = ObjectDB : : get_instance ( id ) ;
if ( obj ) {
2020-02-12 20:06:30 +01:00
ERR_PRINT ( " Leaked unsafe reference to object: " + obj - > to_string ( ) ) ;
2020-01-13 21:00:07 +01:00
} else {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Leaked unsafe reference to deleted object: " + itos ( id ) ) ;
2020-01-13 21:00:07 +01:00
}
}
# endif
2020-03-14 19:20:17 +01:00
memdelete ( managed_callable_middleman ) ;
2017-12-31 22:37:57 +01:00
finalizing = false ;
2020-03-14 19:20:17 +01:00
finalized = true ;
2017-10-02 23:24:00 +02:00
}
void CSharpLanguage : : get_reserved_words ( List < String > * p_words ) const {
static const char * _reserved_words [ ] = {
// Reserved keywords
" abstract " ,
" as " ,
" base " ,
" bool " ,
" break " ,
" byte " ,
" case " ,
" catch " ,
" char " ,
" checked " ,
" class " ,
" const " ,
" continue " ,
" decimal " ,
" default " ,
" delegate " ,
" do " ,
" double " ,
" else " ,
" enum " ,
" event " ,
" explicit " ,
" extern " ,
" false " ,
" finally " ,
" fixed " ,
" float " ,
" for " ,
2018-01-30 21:18:48 -05:00
" foreach " ,
2017-10-02 23:24:00 +02:00
" goto " ,
" if " ,
" implicit " ,
" in " ,
" int " ,
" interface " ,
" internal " ,
" is " ,
" lock " ,
" long " ,
" namespace " ,
" new " ,
" null " ,
" object " ,
" operator " ,
" out " ,
" override " ,
" params " ,
" private " ,
" protected " ,
" public " ,
" readonly " ,
" ref " ,
" return " ,
" sbyte " ,
" sealed " ,
" short " ,
" sizeof " ,
" stackalloc " ,
" static " ,
" string " ,
" struct " ,
" switch " ,
" this " ,
" throw " ,
" true " ,
" try " ,
" typeof " ,
" uint " ,
" ulong " ,
" unchecked " ,
" unsafe " ,
" ushort " ,
" using " ,
" virtual " ,
" void " ,
2018-01-30 21:18:48 -05:00
" volatile " ,
2017-10-02 23:24:00 +02:00
" while " ,
// Contextual keywords. Not reserved words, but I guess we should include
// them because this seems to be used only for syntax highlighting.
" add " ,
2018-01-30 21:18:48 -05:00
" alias " ,
2017-10-02 23:24:00 +02:00
" ascending " ,
2018-01-30 21:18:48 -05:00
" async " ,
" await " ,
2017-10-02 23:24:00 +02:00
" by " ,
" descending " ,
" dynamic " ,
" equals " ,
" from " ,
" get " ,
" global " ,
" group " ,
" into " ,
" join " ,
" let " ,
2018-01-30 21:18:48 -05:00
" nameof " ,
2017-10-02 23:24:00 +02:00
" on " ,
" orderby " ,
" partial " ,
" remove " ,
" select " ,
" set " ,
" value " ,
" var " ,
2018-01-30 21:18:48 -05:00
" when " ,
2017-10-02 23:24:00 +02:00
" where " ,
" yield " ,
2020-05-14 10:15:48 +02:00
nullptr
2017-10-02 23:24:00 +02:00
} ;
const char * * w = _reserved_words ;
while ( * w ) {
p_words - > push_back ( * w ) ;
w + + ;
}
}
2021-04-08 16:12:22 +02:00
bool CSharpLanguage : : is_control_flow_keyword ( String p_keyword ) const {
return p_keyword = = " break " | |
2021-10-28 15:19:35 +02:00
p_keyword = = " case " | |
p_keyword = = " catch " | |
p_keyword = = " continue " | |
p_keyword = = " default " | |
p_keyword = = " do " | |
p_keyword = = " else " | |
p_keyword = = " finally " | |
p_keyword = = " for " | |
p_keyword = = " foreach " | |
p_keyword = = " goto " | |
p_keyword = = " if " | |
p_keyword = = " return " | |
p_keyword = = " switch " | |
p_keyword = = " throw " | |
p_keyword = = " try " | |
p_keyword = = " while " ;
2021-04-08 16:12:22 +02:00
}
2017-10-02 23:24:00 +02:00
void CSharpLanguage : : get_comment_delimiters ( List < String > * p_delimiters ) const {
p_delimiters - > push_back ( " // " ) ; // single-line comment
p_delimiters - > push_back ( " /* */ " ) ; // delimited comment
}
2023-02-05 12:01:01 +03:00
void CSharpLanguage : : get_doc_comment_delimiters ( List < String > * p_delimiters ) const {
p_delimiters - > push_back ( " /// " ) ; // single-line doc comment
p_delimiters - > push_back ( " /** */ " ) ; // delimited doc comment
}
2017-10-02 23:24:00 +02:00
void CSharpLanguage : : get_string_delimiters ( List < String > * p_delimiters ) const {
p_delimiters - > push_back ( " ' ' " ) ; // character literal
p_delimiters - > push_back ( " \" \" " ) ; // regular string literal
2021-08-30 11:38:03 +01:00
p_delimiters - > push_back ( " @ \" \" " ) ; // verbatim string literal
2020-04-01 09:12:47 +02:00
// Generic string highlighting suffices as a workaround for now.
2017-10-02 23:24:00 +02:00
}
2018-03-09 23:34:32 +01:00
static String get_base_class_name ( const String & p_base_class_name , const String p_class_name ) {
2022-12-07 16:11:39 +01:00
String base_class = pascal_to_pascal_case ( p_base_class_name ) ;
2018-03-09 23:34:32 +01:00
if ( p_class_name = = base_class ) {
base_class = " Godot. " + base_class ;
}
return base_class ;
}
2021-10-11 11:30:59 +02:00
bool CSharpLanguage : : is_using_templates ( ) {
return true ;
}
2017-10-02 23:24:00 +02:00
2021-10-11 11:30:59 +02:00
Ref < Script > CSharpLanguage : : make_template ( const String & p_template , const String & p_class_name , const String & p_base_class_name ) const {
2023-03-24 12:38:00 +01:00
Ref < CSharpScript > scr ;
scr . instantiate ( ) ;
2017-10-02 23:24:00 +02:00
2021-10-11 11:30:59 +02:00
String class_name_no_spaces = p_class_name . replace ( " " , " _ " ) ;
String base_class_name = get_base_class_name ( p_base_class_name , class_name_no_spaces ) ;
String processed_template = p_template ;
processed_template = processed_template . replace ( " _BINDINGS_NAMESPACE_ " , BINDINGS_NAMESPACE )
. replace ( " _BASE_ " , base_class_name )
. replace ( " _CLASS_ " , class_name_no_spaces )
. replace ( " _TS_ " , _get_indentation ( ) ) ;
2023-03-24 12:38:00 +01:00
scr - > set_source_code ( processed_template ) ;
return scr ;
2017-10-02 23:24:00 +02:00
}
2021-10-11 11:30:59 +02:00
Vector < ScriptLanguage : : ScriptTemplate > CSharpLanguage : : get_built_in_templates ( StringName p_object ) {
Vector < ScriptLanguage : : ScriptTemplate > templates ;
2022-03-28 15:48:38 +02:00
# ifdef TOOLS_ENABLED
2021-10-11 11:30:59 +02:00
for ( int i = 0 ; i < TEMPLATES_ARRAY_SIZE ; i + + ) {
if ( TEMPLATES [ i ] . inherit = = p_object ) {
templates . append ( TEMPLATES [ i ] ) ;
}
}
2022-03-28 15:48:38 +02:00
# endif
2021-10-11 11:30:59 +02:00
return templates ;
2017-10-30 22:17:20 +01:00
}
2018-03-09 23:34:32 +01:00
String CSharpLanguage : : validate_path ( const String & p_path ) const {
String class_name = p_path . get_file ( ) . get_basename ( ) ;
List < String > keywords ;
get_reserved_words ( & keywords ) ;
if ( keywords . find ( class_name ) ) {
2022-03-28 15:24:14 +02:00
return RTR ( " Class name can't be a reserved keyword " ) ;
2018-03-09 23:34:32 +01:00
}
2023-03-03 18:25:15 -05:00
if ( ! TS - > is_valid_identifier ( class_name ) ) {
return RTR ( " Class name must be a valid identifier " ) ;
}
2018-03-09 23:34:32 +01:00
return " " ;
}
2017-10-02 23:24:00 +02:00
Script * CSharpLanguage : : create_script ( ) const {
return memnew ( CSharpScript ) ;
}
2017-10-24 01:54:47 +02:00
bool CSharpLanguage : : supports_builtin_mode ( ) const {
return false ;
2017-10-02 23:24:00 +02:00
}
2018-10-22 19:43:19 +02:00
# ifdef TOOLS_ENABLED
2022-12-07 16:11:39 +01:00
struct VariantCsName {
Variant : : Type variant_type ;
const String cs_type ;
} ;
2017-10-16 04:17:06 +02:00
static String variant_type_to_managed_name ( const String & p_var_type_name ) {
2020-12-15 12:04:21 +00:00
if ( p_var_type_name . is_empty ( ) ) {
2022-10-07 12:11:24 +02:00
return " Variant " ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
2022-10-07 12:11:24 +02:00
if ( ClassDB : : class_exists ( p_var_type_name ) ) {
2022-12-07 16:11:39 +01:00
return pascal_to_pascal_case ( p_var_type_name ) ;
2018-11-30 20:43:06 +01:00
}
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : OBJECT ) ) {
2022-12-07 16:16:51 +01:00
return " GodotObject " ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
2022-10-07 12:11:24 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : INT ) ) {
return " long " ;
}
2020-02-24 15:20:53 -03:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : FLOAT ) ) {
2018-11-30 20:43:06 +01:00
return " double " ;
}
2017-10-16 04:17:06 +02:00
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : STRING ) ) {
2018-11-30 20:43:06 +01:00
return " string " ; // I prefer this one >:[
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : DICTIONARY ) ) {
2018-11-30 20:43:06 +01:00
return " Collections.Dictionary " ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
2023-08-18 07:35:58 +03:00
if ( p_var_type_name . begins_with ( Variant : : get_type_name ( Variant : : ARRAY ) + " [ " ) ) {
String element_type = p_var_type_name . trim_prefix ( Variant : : get_type_name ( Variant : : ARRAY ) + " [ " ) . trim_suffix ( " ] " ) ;
return " Collections.Array< " + variant_type_to_managed_name ( element_type ) + " > " ;
}
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " Collections.Array " ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_BYTE_ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " byte[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_INT32_ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " int[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_INT64_ARRAY ) ) {
2020-03-14 19:20:17 +01:00
return " long[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_FLOAT32_ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " float[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_FLOAT64_ARRAY ) ) {
2020-03-14 19:20:17 +01:00
return " double[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_STRING_ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " string[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_VECTOR2_ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " Vector2[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_VECTOR3_ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " Vector3[] " ;
2020-07-05 19:19:36 +02:00
}
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : PACKED_COLOR_ARRAY ) ) {
2018-11-30 20:43:06 +01:00
return " Color[] " ;
2020-07-05 19:19:36 +02:00
}
2018-11-30 20:43:06 +01:00
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : SIGNAL ) ) {
2022-12-23 19:28:34 +01:00
return " Signal " ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
2022-12-07 16:11:39 +01:00
const VariantCsName var_types [ ] = {
{ Variant : : BOOL , " bool " } ,
{ Variant : : INT , " long " } ,
{ Variant : : VECTOR2 , " Vector2 " } ,
{ Variant : : VECTOR2I , " Vector2I " } ,
{ Variant : : RECT2 , " Rect2 " } ,
{ Variant : : RECT2I , " Rect2I " } ,
{ Variant : : VECTOR3 , " Vector3 " } ,
{ Variant : : VECTOR3I , " Vector3I " } ,
{ Variant : : TRANSFORM2D , " Transform2D " } ,
{ Variant : : VECTOR4 , " Vector4 " } ,
{ Variant : : VECTOR4I , " Vector4I " } ,
{ Variant : : PLANE , " Plane " } ,
{ Variant : : QUATERNION , " Quaternion " } ,
{ Variant : : AABB , " Aabb " } ,
{ Variant : : BASIS , " Basis " } ,
{ Variant : : TRANSFORM3D , " Transform3D " } ,
{ Variant : : PROJECTION , " Projection " } ,
{ Variant : : COLOR , " Color " } ,
{ Variant : : STRING_NAME , " StringName " } ,
{ Variant : : NODE_PATH , " NodePath " } ,
{ Variant : : RID , " Rid " } ,
{ Variant : : CALLABLE , " Callable " } ,
2018-11-30 20:43:06 +01:00
} ;
2017-10-16 04:17:06 +02:00
2022-12-07 16:11:39 +01:00
for ( unsigned int i = 0 ; i < sizeof ( var_types ) / sizeof ( VariantCsName ) ; i + + ) {
if ( p_var_type_name = = Variant : : get_type_name ( var_types [ i ] . variant_type ) ) {
return var_types [ i ] . cs_type ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
}
2017-10-02 23:24:00 +02:00
2022-10-07 12:11:24 +02:00
return " Variant " ;
2017-10-16 04:17:06 +02:00
}
2020-02-17 18:06:54 -03:00
String CSharpLanguage : : make_function ( const String & , const String & p_name , const PackedStringArray & p_args ) const {
2017-10-02 23:24:00 +02:00
// FIXME
2017-10-16 04:17:06 +02:00
// - Due to Godot's API limitation this just appends the function to the end of the file
// - Use fully qualified name if there is ambiguity
2017-10-02 23:24:00 +02:00
String s = " private void " + p_name + " ( " ;
for ( int i = 0 ; i < p_args . size ( ) ; i + + ) {
2017-10-16 04:17:06 +02:00
const String & arg = p_args [ i ] ;
2020-07-05 19:19:36 +02:00
if ( i > 0 ) {
2017-10-02 23:24:00 +02:00
s + = " , " ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
s + = variant_type_to_managed_name ( arg . get_slice ( " : " , 1 ) ) + " " + escape_csharp_keyword ( arg . get_slice ( " : " , 0 ) ) ;
2017-10-02 23:24:00 +02:00
}
2018-05-23 18:11:00 -03:00
s + = " ) \n { \n // Replace with function body. \n } \n " ;
2017-10-02 23:24:00 +02:00
return s ;
2018-10-22 19:43:19 +02:00
}
2017-10-16 04:17:06 +02:00
# else
2020-02-17 18:06:54 -03:00
String CSharpLanguage : : make_function ( const String & , const String & , const PackedStringArray & ) const {
2017-10-16 04:17:06 +02:00
return String ( ) ;
2017-10-02 23:24:00 +02:00
}
2018-10-22 19:43:19 +02:00
# endif
2017-10-02 23:24:00 +02:00
2017-10-30 22:17:20 +01:00
String CSharpLanguage : : _get_indentation ( ) const {
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2022-03-06 21:39:19 +01:00
bool use_space_indentation = EDITOR_GET ( " text_editor/behavior/indent/type " ) ;
2017-10-30 22:17:20 +01:00
if ( use_space_indentation ) {
2022-03-06 21:39:19 +01:00
int indent_size = EDITOR_GET ( " text_editor/behavior/indent/size " ) ;
2023-01-29 00:52:05 +01:00
return String ( " " ) . repeat ( indent_size ) ;
2017-10-30 22:17:20 +01:00
}
}
# endif
return " \t " ;
}
2023-02-02 00:54:16 +01:00
bool CSharpLanguage : : handles_global_class_type ( const String & p_type ) const {
return p_type = = get_type ( ) ;
}
String CSharpLanguage : : get_global_class_name ( const String & p_path , String * r_base_type , String * r_icon_path ) const {
Ref < CSharpScript > scr = ResourceLoader : : load ( p_path , get_type ( ) ) ;
2023-05-23 09:25:34 -07:00
// Always assign r_base_type and r_icon_path, even if the script
// is not a global one. In the case that it is not a global script,
// return an empty string AFTER assigning the return parameters.
// See GDScriptLanguage::get_global_class_name() in modules/gdscript/gdscript.cpp
2023-02-02 00:54:16 +01:00
2023-05-23 09:25:34 -07:00
if ( ! scr . is_valid ( ) | | ! scr - > valid ) {
// Invalid script.
2023-02-02 00:54:16 +01:00
return String ( ) ;
}
if ( r_icon_path ) {
if ( scr - > icon_path . is_empty ( ) | | scr - > icon_path . is_absolute_path ( ) ) {
* r_icon_path = scr - > icon_path . simplify_path ( ) ;
} else if ( scr - > icon_path . is_relative_path ( ) ) {
* r_icon_path = p_path . get_base_dir ( ) . path_join ( scr - > icon_path ) . simplify_path ( ) ;
}
}
if ( r_base_type ) {
bool found_global_base_script = false ;
const CSharpScript * top = scr - > base_script . ptr ( ) ;
while ( top ! = nullptr ) {
if ( top - > global_class ) {
* r_base_type = top - > class_name ;
found_global_base_script = true ;
break ;
}
top = top - > base_script . ptr ( ) ;
}
if ( ! found_global_base_script ) {
* r_base_type = scr - > get_instance_base_type ( ) ;
}
}
2023-05-23 09:25:34 -07:00
return scr - > global_class ? scr - > class_name : String ( ) ;
2023-02-02 00:54:16 +01:00
}
2019-03-07 20:47:13 +01:00
String CSharpLanguage : : debug_get_error ( ) const {
return _debug_error ;
}
int CSharpLanguage : : debug_get_stack_level_count ( ) const {
2020-07-05 19:19:36 +02:00
if ( _debug_parse_err_line > = 0 ) {
2019-03-07 20:47:13 +01:00
return 1 ;
2020-07-05 19:19:36 +02:00
}
2019-03-07 20:47:13 +01:00
// TODO: StackTrace
return 1 ;
}
int CSharpLanguage : : debug_get_stack_level_line ( int p_level ) const {
2020-07-05 19:19:36 +02:00
if ( _debug_parse_err_line > = 0 ) {
2019-03-07 20:47:13 +01:00
return _debug_parse_err_line ;
2020-07-05 19:19:36 +02:00
}
2019-03-07 20:47:13 +01:00
// TODO: StackTrace
return 1 ;
}
String CSharpLanguage : : debug_get_stack_level_function ( int p_level ) const {
2020-07-05 19:19:36 +02:00
if ( _debug_parse_err_line > = 0 ) {
2019-03-07 20:47:13 +01:00
return String ( ) ;
2020-07-05 19:19:36 +02:00
}
2019-03-07 20:47:13 +01:00
// TODO: StackTrace
return String ( ) ;
}
String CSharpLanguage : : debug_get_stack_level_source ( int p_level ) const {
2020-07-05 19:19:36 +02:00
if ( _debug_parse_err_line > = 0 ) {
2019-03-07 20:47:13 +01:00
return _debug_parse_err_file ;
2020-07-05 19:19:36 +02:00
}
2019-03-07 20:47:13 +01:00
// TODO: StackTrace
return String ( ) ;
}
2018-01-09 17:19:03 +01:00
Vector < ScriptLanguage : : StackInfo > CSharpLanguage : : debug_get_current_stack_info ( ) {
2018-01-27 18:44:04 +01:00
# ifdef DEBUG_ENABLED
2020-03-14 19:20:17 +01:00
// Printing an error here will result in endless recursion, so we must be careful
static thread_local bool _recursion_flag_ = false ;
2020-07-05 19:19:36 +02:00
if ( _recursion_flag_ ) {
2020-03-14 19:20:17 +01:00
return Vector < StackInfo > ( ) ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
_recursion_flag_ = true ;
2021-05-03 15:21:06 +02:00
SCOPE_EXIT {
_recursion_flag_ = false ;
} ;
2020-03-14 19:20:17 +01:00
2023-02-28 22:08:19 +01:00
if ( ! gdmono | | ! gdmono - > is_runtime_initialized ( ) ) {
2018-01-09 17:19:03 +01:00
return Vector < StackInfo > ( ) ;
2020-07-05 19:19:36 +02:00
}
2018-01-09 17:19:03 +01:00
Vector < StackInfo > si ;
2022-02-27 21:57:39 +01:00
if ( GDMonoCache : : godot_api_cache_updated ) {
GDMonoCache : : managed_callbacks . DebuggingUtils_GetCurrentStackInfo ( & si ) ;
2018-01-09 17:19:03 +01:00
}
return si ;
2021-09-12 20:21:15 +02:00
# else
return Vector < StackInfo > ( ) ;
# endif
2022-02-27 21:57:39 +01:00
}
2018-01-09 17:19:03 +01:00
2020-01-13 21:00:07 +01:00
void CSharpLanguage : : post_unsafe_reference ( Object * p_obj ) {
# ifdef DEBUG_ENABLED
2020-02-26 11:28:13 +01:00
MutexLock lock ( unsafe_object_references_lock ) ;
2020-01-13 21:00:07 +01:00
ObjectID id = p_obj - > get_instance_id ( ) ;
2020-01-23 21:23:32 +01:00
unsafe_object_references [ id ] + + ;
2020-01-13 21:00:07 +01:00
# endif
}
void CSharpLanguage : : pre_unsafe_unreference ( Object * p_obj ) {
# ifdef DEBUG_ENABLED
2020-02-26 11:28:13 +01:00
MutexLock lock ( unsafe_object_references_lock ) ;
2020-01-13 21:00:07 +01:00
ObjectID id = p_obj - > get_instance_id ( ) ;
2022-05-13 15:04:37 +02:00
HashMap < ObjectID , int > : : Iterator elem = unsafe_object_references . find ( id ) ;
2020-01-13 21:00:07 +01:00
ERR_FAIL_NULL ( elem ) ;
2022-05-13 15:04:37 +02:00
if ( - - elem - > value = = 0 ) {
unsafe_object_references . remove ( elem ) ;
2020-07-05 19:19:36 +02:00
}
2020-01-13 21:00:07 +01:00
# endif
}
2017-10-02 23:24:00 +02:00
void CSharpLanguage : : frame ( ) {
2021-09-12 20:23:05 +02:00
if ( gdmono & & gdmono - > is_runtime_initialized ( ) & & GDMonoCache : : godot_api_cache_updated ) {
GDMonoCache : : managed_callbacks . ScriptManagerBridge_FrameCallback ( ) ;
2017-10-02 23:24:00 +02:00
}
}
2022-05-28 04:56:46 +02:00
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 ) {
// Shouldn't happen but just in case...
return false ;
}
const Script * I = B - > get_base_script ( ) . ptr ( ) ;
while ( I ) {
if ( I = = A . ptr ( ) ) {
// A is a base of B
return true ;
}
I = I - > get_base_script ( ) . ptr ( ) ;
}
// A isn't a base of B
return false ;
}
} ;
2017-10-02 23:24:00 +02:00
void CSharpLanguage : : reload_all_scripts ( ) {
2019-01-22 18:33:36 +01:00
# ifdef GD_MONO_HOT_RELOAD
2019-01-21 22:44:05 +01:00
if ( is_assembly_reloading_needed ( ) ) {
reload_assemblies ( false ) ;
2017-10-02 23:24:00 +02:00
}
# endif
}
void CSharpLanguage : : reload_tool_script ( const Ref < Script > & p_script , bool p_soft_reload ) {
( void ) p_script ; // UNUSED
2019-01-22 18:33:36 +01:00
CRASH_COND ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ) ;
2017-10-02 23:24:00 +02:00
# ifdef TOOLS_ENABLED
2019-07-03 09:44:53 +02:00
get_godotsharp_editor ( ) - > get_node ( NodePath ( " HotReloadAssemblyWatcher " ) ) - > call ( " RestartTimer " ) ;
2019-01-22 18:33:36 +01:00
# endif
# ifdef GD_MONO_HOT_RELOAD
2018-11-30 20:43:06 +01:00
if ( is_assembly_reloading_needed ( ) ) {
reload_assemblies ( p_soft_reload ) ;
}
2017-10-02 23:24:00 +02:00
# endif
}
2019-01-22 18:33:36 +01:00
# ifdef GD_MONO_HOT_RELOAD
2018-11-30 20:43:06 +01:00
bool CSharpLanguage : : is_assembly_reloading_needed ( ) {
2023-02-28 22:08:19 +01:00
ERR_FAIL_NULL_V ( gdmono , false ) ;
2020-07-05 19:19:36 +02:00
if ( ! gdmono - > is_runtime_initialized ( ) ) {
2018-11-30 20:43:06 +01:00
return false ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
2022-05-28 04:56:46 +02:00
String assembly_path = gdmono - > get_project_assembly_path ( ) ;
2017-10-02 23:24:00 +02:00
2022-05-28 04:56:46 +02:00
if ( ! assembly_path . is_empty ( ) ) {
if ( ! FileAccess : : exists ( assembly_path ) ) {
return false ; // No assembly to load
2017-10-02 23:24:00 +02:00
}
2018-07-25 21:54:21 +02:00
2022-05-28 04:56:46 +02:00
if ( FileAccess : : get_modified_time ( assembly_path ) < = gdmono - > get_project_assembly_modified_time ( ) ) {
2018-11-30 20:43:06 +01:00
return false ; // Already up to date
2020-07-05 19:19:36 +02:00
}
2018-07-25 21:54:21 +02:00
} else {
2023-03-06 21:39:24 +01:00
String assembly_name = path : : get_csharp_project_name ( ) ;
2022-05-28 04:56:46 +02:00
assembly_path = GodotSharpDirs : : get_res_temp_assemblies_dir ( )
2022-08-29 19:34:01 -05:00
. path_join ( assembly_name + " .dll " ) ;
2022-05-28 04:56:46 +02:00
assembly_path = ProjectSettings : : get_singleton ( ) - > globalize_path ( assembly_path ) ;
if ( ! FileAccess : : exists ( assembly_path ) ) {
2018-11-30 20:43:06 +01:00
return false ; // No assembly to load
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
}
2018-11-30 20:43:06 +01:00
return true ;
}
void CSharpLanguage : : reload_assemblies ( bool p_soft_reload ) {
2023-02-28 22:08:19 +01:00
ERR_FAIL_NULL ( gdmono ) ;
2020-07-05 19:19:36 +02:00
if ( ! gdmono - > is_runtime_initialized ( ) ) {
2018-11-30 20:43:06 +01:00
return ;
2020-07-05 19:19:36 +02:00
}
2018-11-30 20:43:06 +01:00
2022-10-16 23:55:54 +02:00
if ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
// We disable collectible assemblies in the game player, because the limitations cause
// issues with mocking libraries. As such, we can only reload assemblies in the editor.
return ;
}
2022-05-28 04:56:46 +02:00
print_verbose ( " .NET: Reloading assemblies... " ) ;
2021-12-28 23:25:16 +01:00
2018-11-30 20:43:06 +01:00
// There is no soft reloading with Mono. It's always hard reloading.
2017-10-02 23:24:00 +02:00
2020-03-17 07:33:00 +01:00
List < Ref < CSharpScript > > scripts ;
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
{
2020-02-26 11:28:13 +01:00
MutexLock lock ( script_instances_mutex ) ;
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
for ( SelfList < CSharpScript > * elem = script_list . first ( ) ; elem ; elem = elem - > next ( ) ) {
2023-06-28 11:46:43 +02:00
// Do not reload scripts with only non-collectible instances to avoid disrupting event subscriptions and such.
bool is_reloadable = elem - > self ( ) - > instances . size ( ) = = 0 ;
2023-03-31 11:53:16 +02:00
for ( Object * obj : elem - > self ( ) - > instances ) {
ERR_CONTINUE ( ! obj - > get_script_instance ( ) ) ;
CSharpInstance * csi = static_cast < CSharpInstance * > ( obj - > get_script_instance ( ) ) ;
if ( GDMonoCache : : managed_callbacks . GCHandleBridge_GCHandleIsTargetCollectible ( csi - > get_gchandle_intptr ( ) ) ) {
is_reloadable = true ;
break ;
}
}
if ( is_reloadable ) {
// Cast to CSharpScript to avoid being erased by accident.
scripts . push_back ( Ref < CSharpScript > ( elem - > self ( ) ) ) ;
}
2017-10-02 23:24:00 +02:00
}
}
2020-03-14 19:20:17 +01:00
scripts . sort_custom < CSharpScriptDepSort > ( ) ; // Update in inheritance dependency order
// Serialize managed callables
{
MutexLock lock ( ManagedCallable : : instances_mutex ) ;
for ( SelfList < ManagedCallable > * elem = ManagedCallable : : instances . first ( ) ; elem ; elem = elem - > next ( ) ) {
ManagedCallable * managed_callable = elem - > self ( ) ;
2022-05-28 04:56:46 +02:00
ERR_CONTINUE ( managed_callable - > delegate_handle . value = = nullptr ) ;
2020-03-14 19:20:17 +01:00
2023-03-31 11:53:16 +02:00
if ( ! GDMonoCache : : managed_callbacks . GCHandleBridge_GCHandleIsTargetCollectible ( managed_callable - > delegate_handle ) ) {
continue ;
}
2022-05-28 04:56:46 +02:00
Array serialized_data ;
2020-03-14 19:20:17 +01:00
2022-05-28 04:56:46 +02:00
bool success = GDMonoCache : : managed_callbacks . DelegateUtils_TrySerializeDelegateWithGCHandle (
managed_callable - > delegate_handle , & serialized_data ) ;
2020-03-14 19:20:17 +01:00
if ( success ) {
ManagedCallable : : instances_pending_reload . insert ( managed_callable , serialized_data ) ;
} else if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
OS : : get_singleton ( ) - > print ( " Failed to serialize delegate \n " ) ;
}
}
}
2020-03-17 07:33:00 +01:00
List < Ref < CSharpScript > > to_reload ;
2017-10-02 23:24:00 +02:00
2019-07-03 09:44:53 +02:00
// We need to keep reference instances alive during reloading
2021-06-04 18:03:15 +02:00
List < Ref < RefCounted > > rc_instances ;
2019-07-03 09:44:53 +02:00
2021-07-26 21:31:17 +02:00
for ( const KeyValue < Object * , CSharpScriptBinding > & E : script_bindings ) {
const CSharpScriptBinding & script_binding = E . value ;
2021-06-04 18:03:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( script_binding . owner ) ;
if ( rc ) {
rc_instances . push_back ( Ref < RefCounted > ( rc ) ) ;
2019-07-03 09:44:53 +02:00
}
}
2018-11-30 20:43:06 +01:00
// As scripts are going to be reloaded, must proceed without locking here
2017-10-02 23:24:00 +02:00
2023-03-24 12:38:00 +01:00
for ( Ref < CSharpScript > & scr : scripts ) {
2021-04-07 00:56:38 -05:00
// If someone removes a script from a node, deletes the script, builds, adds a script to the
// same node, then builds again, the script might have no path and also no script_class. In
// that case, we can't (and don't need to) reload it.
2023-03-24 12:38:00 +01:00
if ( scr - > get_path ( ) . is_empty ( ) & & ! scr - > valid ) {
2021-04-07 00:56:38 -05:00
continue ;
}
2023-03-24 12:38:00 +01:00
to_reload . push_back ( scr ) ;
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
2017-10-02 23:24:00 +02:00
2023-03-24 12:38:00 +01:00
for ( Object * obj : scr - > instances ) {
scr - > pending_reload_instances . insert ( obj - > get_instance_id ( ) ) ;
2019-07-03 09:44:53 +02:00
2022-09-02 22:31:56 +02:00
// Since this script instance wasn't a placeholder, add it to the list of placeholders
// that will have to be eventually replaced with a script instance in case it turns into one.
// This list is not cleared after the reload and the collected instances only leave
// the list if the script is instantiated or if it was a tool script but becomes a
// non-tool script in a rebuild.
2023-03-24 12:38:00 +01:00
scr - > pending_replace_placeholders . insert ( obj - > get_instance_id ( ) ) ;
2022-09-02 22:31:56 +02:00
2021-06-04 18:03:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( obj ) ;
if ( rc ) {
rc_instances . push_back ( Ref < RefCounted > ( rc ) ) ;
2019-07-03 09:44:53 +02:00
}
2018-11-30 20:43:06 +01:00
}
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
# ifdef TOOLS_ENABLED
2023-03-24 12:38:00 +01:00
for ( PlaceHolderScriptInstance * instance : scr - > placeholders ) {
Object * obj = instance - > get_owner ( ) ;
scr - > pending_reload_instances . insert ( obj - > get_instance_id ( ) ) ;
2019-07-03 09:44:53 +02:00
2021-06-04 18:03:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( obj ) ;
if ( rc ) {
rc_instances . push_back ( Ref < RefCounted > ( rc ) ) ;
2019-07-03 09:44:53 +02:00
}
2018-11-30 20:43:06 +01:00
}
# endif
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
// Save state and remove script from instances
2023-03-24 12:38:00 +01:00
RBMap < ObjectID , CSharpScript : : StateBackup > & owners_map = scr - > pending_reload_state ;
2017-10-02 23:24:00 +02:00
2023-03-24 12:38:00 +01:00
for ( Object * obj : scr - > instances ) {
2018-11-30 20:43:06 +01:00
ERR_CONTINUE ( ! obj - > get_script_instance ( ) ) ;
2017-10-02 23:24:00 +02:00
2019-07-03 09:44:53 +02:00
CSharpInstance * csi = static_cast < CSharpInstance * > ( obj - > get_script_instance ( ) ) ;
2017-10-02 23:24:00 +02:00
2022-05-28 04:56:46 +02:00
// Call OnBeforeSerialize and save instance info
2019-07-03 09:44:53 +02:00
CSharpScript : : StateBackup state ;
2022-05-28 04:56:46 +02:00
Dictionary properties ;
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_SerializeState (
csi - > get_gchandle_intptr ( ) , & properties , & state . event_signals ) ;
for ( const Variant * s = properties . next ( nullptr ) ; s ! = nullptr ; s = properties . next ( s ) ) {
StringName name = * s ;
Variant value = properties [ * s ] ;
state . properties . push_back ( Pair < StringName , Variant > ( name , value ) ) ;
}
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
owners_map [ obj - > get_instance_id ( ) ] = state ;
2019-07-03 09:44:53 +02:00
}
}
// After the state of all instances is saved, clear scripts and script instances
2023-03-24 12:38:00 +01:00
for ( Ref < CSharpScript > & scr : scripts ) {
while ( scr - > instances . begin ( ) ) {
Object * obj = * scr - > instances . begin ( ) ;
2022-05-03 01:43:50 +02:00
obj - > set_script ( Ref < RefCounted > ( ) ) ; // Remove script and existing script instances (placeholder are not removed before domain reload)
2017-10-02 23:24:00 +02:00
}
2018-11-30 20:43:06 +01:00
2023-03-24 12:38:00 +01:00
scr - > was_tool_before_reload = scr - > tool ;
scr - > _clear ( ) ;
2017-10-02 23:24:00 +02:00
}
2023-03-31 11:53:16 +02:00
// Release the delegates that were serialized earlier.
{
MutexLock lock ( ManagedCallable : : instances_mutex ) ;
for ( KeyValue < ManagedCallable * , Array > & kv : ManagedCallable : : instances_pending_reload ) {
kv . key - > release_delegate_handle ( ) ;
}
}
2018-11-30 20:43:06 +01:00
// Do domain reload
2022-05-28 04:56:46 +02:00
if ( gdmono - > reload_project_assemblies ( ) ! = OK ) {
2018-07-29 22:40:09 +02:00
// Failed to reload the scripts domain
// Make sure to add the scripts back to their owners before returning
2021-07-26 17:50:35 +02:00
for ( Ref < CSharpScript > & scr : to_reload ) {
2021-07-26 21:31:17 +02:00
for ( const KeyValue < ObjectID , CSharpScript : : StateBackup > & F : scr - > pending_reload_state ) {
Object * obj = ObjectDB : : get_instance ( F . key ) ;
2018-11-30 20:43:06 +01:00
2020-07-05 19:19:36 +02:00
if ( ! obj ) {
2018-07-29 22:40:09 +02:00
continue ;
2020-07-05 19:19:36 +02:00
}
2018-11-30 20:43:06 +01:00
ObjectID obj_id = obj - > get_instance_id ( ) ;
// Use a placeholder for now to avoid losing the state when saving a scene
PlaceHolderScriptInstance * placeholder = scr - > placeholder_instance_create ( obj ) ;
obj - > set_script_instance ( placeholder ) ;
2019-01-22 18:33:36 +01:00
# ifdef TOOLS_ENABLED
2018-11-30 20:43:06 +01:00
// Even though build didn't fail, this tells the placeholder to keep properties and
// it allows using property_set_fallback for restoring the state without a valid script.
2019-01-10 00:26:00 +01:00
scr - > placeholder_fallback_enabled = true ;
2019-01-22 18:33:36 +01:00
# endif
2018-11-30 20:43:06 +01:00
// Restore Variant properties state, it will be kept by the placeholder until the next script reloading
2021-07-26 21:31:17 +02:00
for ( const Pair < StringName , Variant > & G : scr - > pending_reload_state [ obj_id ] . properties ) {
placeholder - > property_set_fallback ( G . first , G . second , nullptr ) ;
2018-07-29 22:40:09 +02:00
}
2018-11-30 20:43:06 +01:00
scr - > pending_reload_state . erase ( obj_id ) ;
2018-07-29 22:40:09 +02:00
}
2022-05-28 04:56:46 +02:00
scr - > pending_reload_instances . clear ( ) ;
scr - > pending_reload_state . clear ( ) ;
2018-07-29 22:40:09 +02:00
}
2019-07-03 09:44:53 +02:00
2017-10-02 23:24:00 +02:00
return ;
2018-07-29 22:40:09 +02:00
}
2017-10-02 23:24:00 +02:00
2020-03-17 07:33:00 +01:00
List < Ref < CSharpScript > > to_reload_state ;
2019-07-03 09:44:53 +02:00
2023-03-24 12:38:00 +01:00
for ( Ref < CSharpScript > & scr : to_reload ) {
2019-01-22 18:33:36 +01:00
# ifdef TOOLS_ENABLED
2023-03-24 12:38:00 +01:00
scr - > exports_invalidated = true ;
2019-01-22 18:33:36 +01:00
# endif
2019-07-03 09:44:53 +02:00
2023-03-24 12:38:00 +01:00
if ( ! scr - > get_path ( ) . is_empty ( ) ) {
scr - > reload ( p_soft_reload ) ;
2019-07-08 15:18:38 +02:00
2023-03-24 12:38:00 +01:00
if ( ! scr - > valid ) {
scr - > pending_reload_instances . clear ( ) ;
scr - > pending_reload_state . clear ( ) ;
2019-07-08 15:18:38 +02:00
continue ;
}
2019-07-03 09:44:53 +02:00
} else {
2023-03-24 12:38:00 +01:00
bool success = GDMonoCache : : managed_callbacks . ScriptManagerBridge_TryReloadRegisteredScriptWithClass ( scr . ptr ( ) ) ;
2019-07-08 15:22:51 +02:00
2022-05-28 04:56:46 +02:00
if ( ! success ) {
// Couldn't reload
2023-03-24 12:38:00 +01:00
scr - > pending_reload_instances . clear ( ) ;
scr - > pending_reload_state . clear ( ) ;
2019-07-03 09:44:53 +02:00
continue ;
}
}
2023-03-24 12:38:00 +01:00
StringName native_name = scr - > get_instance_base_type ( ) ;
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
{
2023-03-24 12:38:00 +01:00
for ( const ObjectID & obj_id : scr - > pending_reload_instances ) {
2018-11-30 20:43:06 +01:00
Object * obj = ObjectDB : : get_instance ( obj_id ) ;
if ( ! obj ) {
2023-03-24 12:38:00 +01:00
scr - > pending_reload_state . erase ( obj_id ) ;
2019-07-03 09:44:53 +02:00
continue ;
}
if ( ! ClassDB : : is_parent_class ( obj - > get_class_name ( ) , native_name ) ) {
// No longer inherits the same compatible type, can't reload
2023-03-24 12:38:00 +01:00
scr - > pending_reload_state . erase ( obj_id ) ;
2018-11-30 20:43:06 +01:00
continue ;
}
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
ScriptInstance * si = obj - > get_script_instance ( ) ;
2017-10-02 23:24:00 +02:00
2022-09-02 22:31:56 +02:00
// Check if the script must be instantiated or kept as a placeholder
// when the script may not be a tool (see #65266)
2023-03-24 12:38:00 +01:00
bool replace_placeholder = scr - > pending_replace_placeholders . has ( obj - > get_instance_id ( ) ) ;
if ( ! scr - > is_tool ( ) & & scr - > was_tool_before_reload ) {
2022-09-02 22:31:56 +02:00
// The script was a tool before the rebuild so the removal was intentional.
replace_placeholder = false ;
2023-03-24 12:38:00 +01:00
scr - > pending_replace_placeholders . erase ( obj - > get_instance_id ( ) ) ;
2022-09-02 22:31:56 +02:00
}
2018-11-30 20:43:06 +01:00
# ifdef TOOLS_ENABLED
if ( si ) {
// If the script instance is not null, then it must be a placeholder.
// Non-placeholder script instances are removed in godot_icall_Object_Disposed.
CRASH_COND ( ! si - > is_placeholder ( ) ) ;
2023-03-24 12:38:00 +01:00
if ( replace_placeholder | | scr - > is_tool ( ) | | ScriptServer : : is_scripting_enabled ( ) ) {
2022-09-02 22:31:56 +02:00
// Replace placeholder with a script instance.
2018-11-30 20:43:06 +01:00
2023-03-24 12:38:00 +01:00
CSharpScript : : StateBackup & state_backup = scr - > pending_reload_state [ obj_id ] ;
2018-11-30 20:43:06 +01:00
2022-09-02 22:31:56 +02:00
// Backup placeholder script instance state before replacing it with a script instance.
2019-01-21 22:44:05 +01:00
si - > get_property_state ( state_backup . properties ) ;
2018-11-30 20:43:06 +01:00
2023-03-24 12:38:00 +01:00
ScriptInstance * instance = scr - > instance_create ( obj ) ;
2018-11-30 20:43:06 +01:00
2023-03-24 12:38:00 +01:00
if ( instance ) {
scr - > placeholders . erase ( static_cast < PlaceHolderScriptInstance * > ( si ) ) ;
scr - > pending_replace_placeholders . erase ( obj - > get_instance_id ( ) ) ;
obj - > set_script_instance ( instance ) ;
2018-11-30 20:43:06 +01:00
}
}
continue ;
2017-10-02 23:24:00 +02:00
}
2018-11-30 20:43:06 +01:00
# else
2020-04-02 01:20:12 +02:00
CRASH_COND ( si ! = nullptr ) ;
2018-11-30 20:43:06 +01:00
# endif
2022-09-02 22:31:56 +02:00
// Re-create the script instance.
2023-03-24 12:38:00 +01:00
if ( replace_placeholder | | scr - > is_tool ( ) | | ScriptServer : : is_scripting_enabled ( ) ) {
2022-09-02 22:31:56 +02:00
// Create script instance or replace placeholder with a script instance.
2023-03-24 12:38:00 +01:00
ScriptInstance * instance = scr - > instance_create ( obj ) ;
2022-09-02 22:31:56 +02:00
2023-03-24 12:38:00 +01:00
if ( instance ) {
scr - > pending_replace_placeholders . erase ( obj - > get_instance_id ( ) ) ;
obj - > set_script_instance ( instance ) ;
2022-09-02 22:31:56 +02:00
continue ;
}
}
// The script instance could not be instantiated or wasn't in the list of placeholders to replace.
2023-03-24 12:38:00 +01:00
obj - > set_script ( scr ) ;
2022-09-02 22:31:56 +02:00
# if DEBUG_ENABLED
// If we reached here, the instantiated script must be a placeholder.
CRASH_COND ( ! obj - > get_script_instance ( ) - > is_placeholder ( ) ) ;
# endif
2019-07-03 09:44:53 +02:00
}
}
2018-11-30 20:43:06 +01:00
2023-03-24 12:38:00 +01:00
to_reload_state . push_back ( scr ) ;
2019-07-03 09:44:53 +02:00
}
2018-11-30 20:43:06 +01:00
2023-03-24 12:38:00 +01:00
for ( Ref < CSharpScript > & scr : to_reload_state ) {
for ( const ObjectID & obj_id : scr - > pending_reload_instances ) {
2019-07-03 09:44:53 +02:00
Object * obj = ObjectDB : : get_instance ( obj_id ) ;
if ( ! obj ) {
2023-03-24 12:38:00 +01:00
scr - > pending_reload_state . erase ( obj_id ) ;
2019-07-03 09:44:53 +02:00
continue ;
2017-10-02 23:24:00 +02:00
}
2019-07-03 09:44:53 +02:00
ERR_CONTINUE ( ! obj - > get_script_instance ( ) ) ;
2023-03-24 12:38:00 +01:00
CSharpScript : : StateBackup & state_backup = scr - > pending_reload_state [ obj_id ] ;
2019-07-03 09:44:53 +02:00
CSharpInstance * csi = CAST_CSHARP_INSTANCE ( obj - > get_script_instance ( ) ) ;
2020-03-14 19:20:17 +01:00
if ( csi ) {
2022-05-28 04:56:46 +02:00
Dictionary properties ;
2020-03-14 19:20:17 +01:00
2022-05-28 04:56:46 +02:00
for ( const Pair < StringName , Variant > & G : state_backup . properties ) {
properties [ G . first ] = G . second ;
2020-03-14 19:20:17 +01:00
}
2022-05-28 04:56:46 +02:00
// Restore serialized state and call OnAfterDeserialization
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_DeserializeState (
csi - > get_gchandle_intptr ( ) , & properties , & state_backup . event_signals ) ;
2020-03-14 19:20:17 +01:00
}
2017-10-02 23:24:00 +02:00
}
2019-07-03 09:44:53 +02:00
2023-03-24 12:38:00 +01:00
scr - > pending_reload_instances . clear ( ) ;
scr - > pending_reload_state . clear ( ) ;
2017-10-02 23:24:00 +02:00
}
2017-10-29 05:57:20 +01:00
2020-03-14 19:20:17 +01:00
// Deserialize managed callables
{
MutexLock lock ( ManagedCallable : : instances_mutex ) ;
2021-07-26 21:31:17 +02:00
for ( const KeyValue < ManagedCallable * , Array > & elem : ManagedCallable : : instances_pending_reload ) {
ManagedCallable * managed_callable = elem . key ;
const Array & serialized_data = elem . value ;
2020-03-14 19:20:17 +01:00
2022-05-28 04:56:46 +02:00
GCHandleIntPtr delegate = { nullptr } ;
2020-03-14 19:20:17 +01:00
2022-05-28 04:56:46 +02:00
bool success = GDMonoCache : : managed_callbacks . DelegateUtils_TryDeserializeDelegateWithGCHandle (
& serialized_data , & delegate ) ;
2020-03-14 19:20:17 +01:00
if ( success ) {
2022-05-28 04:56:46 +02:00
ERR_CONTINUE ( delegate . value = = nullptr ) ;
2021-05-03 15:21:06 +02:00
managed_callable - > delegate_handle = delegate ;
2020-03-14 19:20:17 +01:00
} else if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
OS : : get_singleton ( ) - > print ( " Failed to deserialize delegate \n " ) ;
}
}
ManagedCallable : : instances_pending_reload . clear ( ) ;
}
2019-01-22 18:33:36 +01:00
# ifdef TOOLS_ENABLED
2018-11-30 20:43:06 +01:00
// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
2018-01-18 18:17:29 +01:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2021-11-17 21:08:55 +01:00
InspectorDock : : get_inspector_singleton ( ) - > update_tree ( ) ;
NodeDock : : get_singleton ( ) - > update_lists ( ) ;
2018-01-18 18:17:29 +01:00
}
2019-01-22 18:33:36 +01:00
# endif
2018-10-22 19:43:19 +02:00
}
2021-09-12 20:21:15 +02:00
# endif
2018-10-22 19:43:19 +02:00
2017-10-02 23:24:00 +02:00
void CSharpLanguage : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " cs " ) ;
}
# ifdef TOOLS_ENABLED
Error CSharpLanguage : : open_in_external_editor ( const Ref < Script > & p_script , int p_line , int p_col ) {
2019-07-03 09:44:53 +02:00
return ( Error ) ( int ) get_godotsharp_editor ( ) - > call ( " OpenInExternalEditor " , p_script , p_line , p_col ) ;
2017-10-02 23:24:00 +02:00
}
bool CSharpLanguage : : overrides_external_editor ( ) {
2019-07-03 09:44:53 +02:00
return get_godotsharp_editor ( ) - > call ( " OverridesExternalEditor " ) ;
2017-10-02 23:24:00 +02:00
}
# endif
bool CSharpLanguage : : debug_break_parse ( const String & p_file , int p_line , const String & p_error ) {
2019-03-07 20:47:13 +01:00
// Not a parser error in our case, but it's still used for other type of errors
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) & & Thread : : get_caller_id ( ) = = Thread : : get_main_id ( ) ) {
2019-03-07 20:47:13 +01:00
_debug_parse_err_line = p_line ;
_debug_parse_err_file = p_file ;
_debug_error = p_error ;
2020-02-27 03:30:20 +01:00
EngineDebugger : : get_script_debugger ( ) - > debug ( this , false , true ) ;
2017-10-02 23:24:00 +02:00
return true ;
} else {
return false ;
}
}
bool CSharpLanguage : : debug_break ( const String & p_error , bool p_allow_continue ) {
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) & & Thread : : get_caller_id ( ) = = Thread : : get_main_id ( ) ) {
2019-03-07 20:47:13 +01:00
_debug_parse_err_line = - 1 ;
_debug_parse_err_file = " " ;
_debug_error = p_error ;
2020-02-27 03:30:20 +01:00
EngineDebugger : : get_script_debugger ( ) - > debug ( this , p_allow_continue ) ;
2017-10-02 23:24:00 +02:00
return true ;
} else {
return false ;
}
}
2019-07-03 09:44:53 +02:00
# ifdef TOOLS_ENABLED
void CSharpLanguage : : _editor_init_callback ( ) {
2021-09-12 20:23:05 +02:00
// Load GodotTools and initialize GodotSharpEditor
2019-07-03 09:44:53 +02:00
2022-08-05 03:32:59 +02:00
int32_t interop_funcs_size = 0 ;
const void * * interop_funcs = godotsharp : : get_editor_interop_funcs ( interop_funcs_size ) ;
2021-12-28 23:25:16 +01:00
Object * editor_plugin_obj = GDMono : : get_singleton ( ) - > get_plugin_callbacks ( ) . LoadToolsAssemblyCallback (
2022-08-29 19:34:01 -05:00
GodotSharpDirs : : get_data_editor_tools_dir ( ) . path_join ( " GodotTools.dll " ) . utf16 ( ) ,
2022-08-05 03:32:59 +02:00
interop_funcs , interop_funcs_size ) ;
2021-09-12 20:23:05 +02:00
CRASH_COND ( editor_plugin_obj = = nullptr ) ;
2021-09-12 20:21:15 +02:00
2021-09-12 20:23:05 +02:00
EditorPlugin * godotsharp_editor = Object : : cast_to < EditorPlugin > ( editor_plugin_obj ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( godotsharp_editor = = nullptr ) ;
2019-07-03 09:44:53 +02:00
2021-09-12 20:23:05 +02:00
// Add plugin to EditorNode and enable it
2019-07-03 09:44:53 +02:00
EditorNode : : add_editor_plugin ( godotsharp_editor ) ;
godotsharp_editor - > enable_plugin ( ) ;
get_singleton ( ) - > godotsharp_editor = godotsharp_editor ;
}
# endif
2017-10-02 23:24:00 +02:00
void CSharpLanguage : : set_language_index ( int p_idx ) {
ERR_FAIL_COND ( lang_idx ! = - 1 ) ;
lang_idx = p_idx ;
}
2019-12-11 17:08:40 +01:00
void CSharpLanguage : : release_script_gchandle ( MonoGCHandleData & p_gchandle ) {
if ( ! p_gchandle . is_released ( ) ) { // Do not lock unnecessarily
2020-02-26 11:28:13 +01:00
MutexLock lock ( get_singleton ( ) - > script_gchandle_release_mutex ) ;
2019-12-11 17:08:40 +01:00
p_gchandle . release ( ) ;
2018-09-12 02:41:54 +02:00
}
}
2022-02-27 21:57:30 +01:00
void CSharpLanguage : : release_script_gchandle_thread_safe ( GCHandleIntPtr p_gchandle_to_free , MonoGCHandleData & r_gchandle ) {
if ( ! r_gchandle . is_released ( ) & & r_gchandle . get_intptr ( ) = = p_gchandle_to_free ) { // Do not lock unnecessarily
MutexLock lock ( get_singleton ( ) - > script_gchandle_release_mutex ) ;
if ( ! r_gchandle . is_released ( ) & & r_gchandle . get_intptr ( ) = = p_gchandle_to_free ) {
r_gchandle . release ( ) ;
}
}
}
2018-09-12 02:41:54 +02:00
2022-02-27 21:57:30 +01:00
void CSharpLanguage : : release_binding_gchandle_thread_safe ( GCHandleIntPtr p_gchandle_to_free , CSharpScriptBinding & r_script_binding ) {
MonoGCHandleData & gchandle = r_script_binding . gchandle ;
if ( ! gchandle . is_released ( ) & & gchandle . get_intptr ( ) = = p_gchandle_to_free ) { // Do not lock unnecessarily
MutexLock lock ( get_singleton ( ) - > script_gchandle_release_mutex ) ;
if ( ! gchandle . is_released ( ) & & gchandle . get_intptr ( ) = = p_gchandle_to_free ) {
gchandle . release ( ) ;
r_script_binding . inited = false ; // Here too, to be thread safe
}
}
2018-09-12 02:41:54 +02:00
}
2017-10-02 23:24:00 +02:00
CSharpLanguage : : CSharpLanguage ( ) {
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( singleton , " C# singleton already exist. " ) ;
2017-10-02 23:24:00 +02:00
singleton = this ;
}
CSharpLanguage : : ~ CSharpLanguage ( ) {
2020-07-05 19:19:36 +02:00
finalize ( ) ;
2020-03-14 19:20:17 +01:00
singleton = nullptr ;
2017-10-02 23:24:00 +02:00
}
2019-02-03 06:35:22 +01:00
bool CSharpLanguage : : setup_csharp_script_binding ( CSharpScriptBinding & r_script_binding , Object * p_object ) {
2017-10-02 23:24:00 +02:00
# ifdef DEBUG_ENABLED
// I don't trust you
2019-02-03 06:35:22 +01:00
if ( p_object - > get_script_instance ( ) ) {
CSharpInstance * csharp_instance = CAST_CSHARP_INSTANCE ( p_object - > get_script_instance ( ) ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( csharp_instance ! = nullptr & & ! csharp_instance - > is_destructing_script_instance ( ) ) ;
2019-02-03 06:35:22 +01:00
}
2017-10-02 23:24:00 +02:00
# endif
StringName type_name = p_object - > get_class_name ( ) ;
2017-10-09 23:50:06 +02:00
const ClassDB : : ClassInfo * classinfo = ClassDB : : classes . getptr ( type_name ) ;
2023-04-12 00:35:10 +02:00
// This skipping of GDExtension classes, as well as whatever classes are in this list of ignored types, is a
// workaround to allow GDExtension classes to be used from C# so long as they're only used through base classes that
// are registered from the engine. This will likely need to be removed whenever proper support for GDExtension
// classes is added to C#. See #75955 for more details.
while ( classinfo & & ( ! classinfo - > exposed | | classinfo - > gdextension | | ignored_types . has ( classinfo - > name ) ) ) {
2017-10-09 23:50:06 +02:00
classinfo = classinfo - > inherits_ptr ;
2020-07-05 19:19:36 +02:00
}
2023-04-12 00:35:10 +02:00
2019-02-03 06:35:22 +01:00
ERR_FAIL_NULL_V ( classinfo , false ) ;
2017-10-09 23:50:06 +02:00
type_name = classinfo - > name ;
2021-09-12 20:21:15 +02:00
bool parent_is_object_class = ClassDB : : is_parent_class ( p_object - > get_class_name ( ) , type_name ) ;
ERR_FAIL_COND_V_MSG ( ! parent_is_object_class , false ,
" Type inherits from native type ' " + type_name + " ', so it can't be instantiated in object of type: ' " + p_object - > get_class ( ) + " '. " ) ;
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:30 +01:00
# ifdef DEBUG_ENABLED
CRASH_COND ( ! r_script_binding . gchandle . is_released ( ) ) ;
# endif
2021-09-12 20:21:15 +02:00
GCHandleIntPtr strong_gchandle =
2022-05-28 04:56:46 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_CreateManagedForGodotObjectBinding (
& type_name , p_object ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
ERR_FAIL_NULL_V ( strong_gchandle . value , false ) ;
2017-10-02 23:24:00 +02:00
2019-02-03 06:35:22 +01:00
r_script_binding . inited = true ;
r_script_binding . type_name = type_name ;
2021-09-12 20:21:15 +02:00
r_script_binding . gchandle = MonoGCHandleData ( strong_gchandle , gdmono : : GCHandleType : : STRONG_HANDLE ) ;
2019-07-03 09:44:53 +02:00
r_script_binding . owner = p_object ;
2017-10-02 23:24:00 +02:00
2018-09-12 02:41:54 +02:00
// Tie managed to unmanaged
2021-06-04 18:03:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( p_object ) ;
2018-09-12 02:41:54 +02:00
2021-06-04 18:03:15 +02:00
if ( rc ) {
2018-09-12 02:41:54 +02:00
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
2021-06-13 11:15:44 +02:00
// See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
2018-09-12 02:41:54 +02:00
2021-06-04 18:03:15 +02:00
rc - > reference ( ) ;
CSharpLanguage : : get_singleton ( ) - > post_unsafe_reference ( rc ) ;
2018-09-12 02:41:54 +02:00
}
2019-02-03 06:35:22 +01:00
return true ;
}
2022-05-13 15:04:37 +02:00
RBMap < Object * , CSharpScriptBinding > : : Element * CSharpLanguage : : insert_script_binding ( Object * p_object , const CSharpScriptBinding & p_script_binding ) {
2021-08-16 17:16:36 +02:00
return script_bindings . insert ( p_object , p_script_binding ) ;
}
void * CSharpLanguage : : _instance_binding_create_callback ( void * , void * p_instance ) {
CSharpLanguage * csharp_lang = CSharpLanguage : : get_singleton ( ) ;
2019-02-04 20:39:02 +01:00
2021-08-16 17:16:36 +02:00
MutexLock lock ( csharp_lang - > language_bind_mutex ) ;
2022-05-13 15:04:37 +02:00
RBMap < Object * , CSharpScriptBinding > : : Element * match = csharp_lang - > script_bindings . find ( ( Object * ) p_instance ) ;
2020-07-05 19:19:36 +02:00
if ( match ) {
2019-02-04 20:39:02 +01:00
return ( void * ) match ;
2020-07-05 19:19:36 +02:00
}
2019-02-04 20:39:02 +01:00
2019-02-03 06:35:22 +01:00
CSharpScriptBinding script_binding ;
2021-08-16 17:16:36 +02:00
return ( void * ) csharp_lang - > insert_script_binding ( ( Object * ) p_instance , script_binding ) ;
2019-02-04 20:39:02 +01:00
}
2019-02-03 06:35:22 +01:00
2021-08-16 17:16:36 +02:00
void CSharpLanguage : : _instance_binding_free_callback ( void * , void * , void * p_binding ) {
CSharpLanguage * csharp_lang = CSharpLanguage : : get_singleton ( ) ;
2017-10-02 23:24:00 +02:00
2020-04-02 01:20:12 +02:00
if ( GDMono : : get_singleton ( ) = = nullptr ) {
2017-11-01 01:48:47 +01:00
# ifdef DEBUG_ENABLED
2021-05-03 15:21:06 +02:00
CRASH_COND ( csharp_lang & & ! csharp_lang - > script_bindings . is_empty ( ) ) ;
2017-11-01 01:48:47 +01:00
# endif
// Mono runtime finalized, all the gchandle bindings were already released
return ;
}
2021-08-16 17:16:36 +02:00
if ( csharp_lang - > finalizing ) {
2017-12-31 22:37:57 +01:00
return ; // inside CSharpLanguage::finish(), all the gchandle bindings are released there
2020-07-05 19:19:36 +02:00
}
2017-12-31 22:37:57 +01:00
2018-11-30 20:43:06 +01:00
{
2021-08-16 17:16:36 +02:00
MutexLock lock ( csharp_lang - > language_bind_mutex ) ;
2017-12-31 22:37:57 +01:00
2022-05-13 15:04:37 +02:00
RBMap < Object * , CSharpScriptBinding > : : Element * data = ( RBMap < Object * , CSharpScriptBinding > : : Element * ) p_binding ;
2017-12-31 22:37:57 +01:00
2019-02-03 06:35:22 +01:00
CSharpScriptBinding & script_binding = data - > value ( ) ;
if ( script_binding . inited ) {
// Set the native instance field to IntPtr.Zero, if not yet garbage collected.
// This is done to avoid trying to dispose the native instance from Dispose(bool).
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_SetGodotObjectPtr (
script_binding . gchandle . get_intptr ( ) , nullptr ) ;
2021-09-12 20:21:15 +02:00
2019-12-11 17:08:40 +01:00
script_binding . gchandle . release ( ) ;
2021-09-12 20:21:15 +02:00
script_binding . inited = false ;
2018-11-30 20:43:06 +01:00
}
2017-10-02 23:24:00 +02:00
2021-08-16 17:16:36 +02:00
csharp_lang - > script_bindings . erase ( data ) ;
2018-11-30 20:43:06 +01:00
}
2017-10-02 23:24:00 +02:00
}
2022-12-07 12:11:28 +01:00
GDExtensionBool CSharpLanguage : : _instance_binding_reference_callback ( void * p_token , void * p_binding , GDExtensionBool p_reference ) {
2021-08-16 17:16:36 +02:00
CRASH_COND ( ! p_binding ) ;
2022-05-13 15:04:37 +02:00
CSharpScriptBinding & script_binding = ( ( RBMap < Object * , CSharpScriptBinding > : : Element * ) p_binding ) - > get ( ) ;
2021-08-16 17:16:36 +02:00
RefCounted * rc_owner = Object : : cast_to < RefCounted > ( script_binding . owner ) ;
2018-02-22 15:34:08 +01:00
# ifdef DEBUG_ENABLED
2021-06-04 18:03:15 +02:00
CRASH_COND ( ! rc_owner ) ;
2018-02-22 15:34:08 +01:00
# endif
2019-12-11 17:08:40 +01:00
MonoGCHandleData & gchandle = script_binding . gchandle ;
2018-02-22 15:34:08 +01:00
2022-09-19 15:45:45 +01:00
int refcount = rc_owner - > get_reference_count ( ) ;
2021-08-16 17:16:36 +02:00
2020-07-05 19:19:36 +02:00
if ( ! script_binding . inited ) {
2021-08-16 17:16:36 +02:00
return refcount = = 0 ;
2020-07-05 19:19:36 +02:00
}
2019-07-03 09:44:53 +02:00
2021-08-16 17:16:36 +02:00
if ( p_reference ) {
// Refcount incremented
if ( refcount > 1 & & gchandle . is_weak ( ) ) { // The managed side also holds a reference, hence 1 instead of 0
// The reference count was increased after the managed side was the only one referencing our owner.
// This means the owner is being referenced again by the unmanaged side,
// so the owner must hold the managed side alive again to avoid it from being GCed.
2021-09-12 20:21:15 +02:00
// Release the current weak handle and replace it with a strong handle.
GCHandleIntPtr old_gchandle = gchandle . get_intptr ( ) ;
2022-02-27 21:57:30 +01:00
gchandle . handle = { nullptr } ; // No longer owns the handle (released by swap function)
2021-09-12 20:21:15 +02:00
2022-02-27 21:57:30 +01:00
GCHandleIntPtr new_gchandle = { nullptr } ;
2021-09-12 20:21:15 +02:00
bool create_weak = false ;
2021-09-12 20:23:05 +02:00
bool target_alive = GDMonoCache : : managed_callbacks . ScriptManagerBridge_SwapGCHandleForType (
old_gchandle , & new_gchandle , create_weak ) ;
2021-09-12 20:21:15 +02:00
if ( ! target_alive ) {
2021-08-16 17:16:36 +02:00
return false ; // Called after the managed side was collected, so nothing to do here
}
2018-02-22 15:34:08 +01:00
2021-09-12 20:21:15 +02:00
gchandle = MonoGCHandleData ( new_gchandle , gdmono : : GCHandleType : : STRONG_HANDLE ) ;
2020-07-05 19:19:36 +02:00
}
2018-02-22 15:34:08 +01:00
2021-08-16 17:16:36 +02:00
return false ;
} else {
// Refcount decremented
if ( refcount = = 1 & & ! gchandle . is_released ( ) & & ! gchandle . is_weak ( ) ) { // The managed side also holds a reference, hence 1 instead of 0
// If owner owner is no longer referenced by the unmanaged side,
// the managed instance takes responsibility of deleting the owner when GCed.
2018-02-22 15:34:08 +01:00
2021-09-12 20:21:15 +02:00
// Release the current strong handle and replace it with a weak handle.
GCHandleIntPtr old_gchandle = gchandle . get_intptr ( ) ;
2022-02-27 21:57:30 +01:00
gchandle . handle = { nullptr } ; // No longer owns the handle (released by swap function)
2021-09-12 20:21:15 +02:00
2022-02-27 21:57:30 +01:00
GCHandleIntPtr new_gchandle = { nullptr } ;
2021-09-12 20:21:15 +02:00
bool create_weak = true ;
2021-09-12 20:23:05 +02:00
bool target_alive = GDMonoCache : : managed_callbacks . ScriptManagerBridge_SwapGCHandleForType (
old_gchandle , & new_gchandle , create_weak ) ;
2021-09-12 20:21:15 +02:00
if ( ! target_alive ) {
2021-08-16 17:16:36 +02:00
return refcount = = 0 ; // Called after the managed side was collected, so nothing to do here
}
2018-02-22 15:34:08 +01:00
2021-09-12 20:21:15 +02:00
gchandle = MonoGCHandleData ( new_gchandle , gdmono : : GCHandleType : : WEAK_HANDLE ) ;
2019-02-03 06:35:22 +01:00
2021-08-16 17:16:36 +02:00
return false ;
}
2019-07-03 09:44:53 +02:00
return refcount = = 0 ;
2020-07-05 19:19:36 +02:00
}
2021-08-16 17:16:36 +02:00
}
2019-07-03 09:44:53 +02:00
2021-08-16 17:16:36 +02:00
void * CSharpLanguage : : get_instance_binding ( Object * p_object ) {
void * binding = p_object - > get_instance_binding ( get_singleton ( ) , & _instance_binding_callbacks ) ;
2020-01-16 17:11:13 +01:00
2021-08-16 17:16:36 +02:00
// Initially this was in `_instance_binding_create_callback`. However, after the new instance
// binding re-write it was resulting in a deadlock in `_instance_binding_reference`, as
// `setup_csharp_script_binding` may call `reference()`. It was moved here outside to fix that.
2018-02-22 15:34:08 +01:00
2021-08-16 17:16:36 +02:00
if ( binding ) {
2022-05-13 15:04:37 +02:00
CSharpScriptBinding & script_binding = ( ( RBMap < Object * , CSharpScriptBinding > : : Element * ) binding ) - > value ( ) ;
2018-02-22 15:34:08 +01:00
2021-08-16 17:16:36 +02:00
if ( ! script_binding . inited ) {
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > get_language_bind_mutex ( ) ) ;
2018-02-22 15:34:08 +01:00
2021-08-16 17:16:36 +02:00
if ( ! script_binding . inited ) { // Another thread may have set it up
CSharpLanguage : : get_singleton ( ) - > setup_csharp_script_binding ( script_binding , p_object ) ;
}
}
2018-02-22 15:34:08 +01:00
}
2021-08-16 17:16:36 +02:00
return binding ;
}
void * CSharpLanguage : : get_existing_instance_binding ( Object * p_object ) {
# ifdef DEBUG_ENABLED
CRASH_COND ( p_object - > has_instance_binding ( p_object ) ) ;
2021-07-08 16:16:02 -03:00
# endif
2021-08-16 17:16:36 +02:00
return p_object - > get_instance_binding ( get_singleton ( ) , & _instance_binding_callbacks ) ;
}
void CSharpLanguage : : set_instance_binding ( Object * p_object , void * p_binding ) {
p_object - > set_instance_binding ( get_singleton ( ) , p_binding , & _instance_binding_callbacks ) ;
}
bool CSharpLanguage : : has_instance_binding ( Object * p_object ) {
return p_object - > has_instance_binding ( get_singleton ( ) ) ;
2018-02-22 15:34:08 +01:00
}
2021-09-12 20:21:15 +02:00
void CSharpLanguage : : tie_native_managed_to_unmanaged ( GCHandleIntPtr p_gchandle_intptr , Object * p_unmanaged , const StringName * p_native_name , bool p_ref_counted ) {
// This method should not fail
2018-02-22 15:34:08 +01:00
2021-09-12 20:21:15 +02:00
CRASH_COND ( ! p_unmanaged ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
// All mono objects created from the managed world (e.g.: 'new Player()')
// need to have a CSharpScript in order for their methods to be callable from the unmanaged side
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( p_unmanaged ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
CRASH_COND ( p_ref_counted ! = ( bool ) rc ) ;
MonoGCHandleData gchandle = MonoGCHandleData ( p_gchandle_intptr ,
p_ref_counted ? gdmono : : GCHandleType : : WEAK_HANDLE : gdmono : : GCHandleType : : STRONG_HANDLE ) ;
// If it's just a wrapper Godot class and not a custom inheriting class, then attach a
// script binding instead. One of the advantages of this is that if a script is attached
// later and it's not a C# script, then the managed object won't have to be disposed.
// Another reason for doing this is that this instance could outlive CSharpLanguage, which would
// be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621
CSharpScriptBinding script_binding ;
script_binding . inited = true ;
script_binding . type_name = * p_native_name ;
script_binding . gchandle = gchandle ;
script_binding . owner = p_unmanaged ;
if ( p_ref_counted ) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
// See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
// May not me referenced yet, so we must use init_ref() instead of reference()
if ( rc - > init_ref ( ) ) {
CSharpLanguage : : get_singleton ( ) - > post_unsafe_reference ( rc ) ;
}
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
// The object was just created, no script instance binding should have been attached
CRASH_COND ( CSharpLanguage : : has_instance_binding ( p_unmanaged ) ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
void * data ;
{
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > get_language_bind_mutex ( ) ) ;
data = ( void * ) CSharpLanguage : : get_singleton ( ) - > insert_script_binding ( p_unmanaged , script_binding ) ;
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
// Should be thread safe because the object was just created and nothing else should be referencing it
CSharpLanguage : : set_instance_binding ( p_unmanaged , data ) ;
2017-10-02 23:24:00 +02:00
}
2022-02-27 21:57:36 +01:00
void CSharpLanguage : : tie_user_managed_to_unmanaged ( GCHandleIntPtr p_gchandle_intptr , Object * p_unmanaged , Ref < CSharpScript > * p_script , bool p_ref_counted ) {
2021-09-12 20:21:15 +02:00
// This method should not fail
2019-02-03 06:35:22 +01:00
2022-02-27 21:57:36 +01:00
Ref < CSharpScript > script = * p_script ;
// We take care of destructing this reference here, so the managed code won't need to do another P/Invoke call
p_script - > ~ Ref ( ) ;
2021-09-12 20:21:15 +02:00
CRASH_COND ( ! p_unmanaged ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
// All mono objects created from the managed world (e.g.: 'new Player()')
// need to have a CSharpScript in order for their methods to be callable from the unmanaged side
2020-01-16 17:11:13 +01:00
2021-09-12 20:21:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( p_unmanaged ) ;
2018-01-04 21:05:46 +01:00
2021-09-12 20:21:15 +02:00
CRASH_COND ( p_ref_counted ! = ( bool ) rc ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
MonoGCHandleData gchandle = MonoGCHandleData ( p_gchandle_intptr ,
p_ref_counted ? gdmono : : GCHandleType : : WEAK_HANDLE : gdmono : : GCHandleType : : STRONG_HANDLE ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
CRASH_COND ( script . is_null ( ) ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
CSharpInstance * csharp_instance = CSharpInstance : : create_for_managed_type ( p_unmanaged , script . ptr ( ) , gchandle ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
p_unmanaged - > set_script_and_instance ( script , csharp_instance ) ;
2022-07-28 17:41:47 +02:00
csharp_instance - > connect_event_signals ( ) ;
2021-09-12 20:21:15 +02:00
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
void CSharpLanguage : : tie_managed_to_unmanaged_with_pre_setup ( GCHandleIntPtr p_gchandle_intptr , Object * p_unmanaged ) {
// This method should not fail
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
CRASH_COND ( ! p_unmanaged ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
CSharpInstance * instance = CAST_CSHARP_INSTANCE ( p_unmanaged - > get_script_instance ( ) ) ;
2018-01-04 21:05:46 +01:00
2021-09-12 20:21:15 +02:00
if ( ! instance ) {
2021-12-28 23:25:16 +01:00
// Native bindings don't need post-setup
2021-09-12 20:21:15 +02:00
return ;
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
CRASH_COND ( ! instance - > gchandle . is_released ( ) ) ;
2018-01-30 18:53:00 +01:00
2021-09-12 20:21:15 +02:00
// Tie managed to unmanaged
instance - > gchandle = MonoGCHandleData ( p_gchandle_intptr , gdmono : : GCHandleType : : STRONG_HANDLE ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
if ( instance - > base_ref_counted ) {
instance - > _reference_owner_unsafe ( ) ; // Here, after assigning the gchandle (for the refcount_incremented callback)
2017-10-02 23:24:00 +02:00
}
2021-09-12 20:21:15 +02:00
{
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > get_script_instances_mutex ( ) ) ;
// instances is a set, so it's safe to insert multiple times (e.g.: from _internal_new_managed)
instance - > script - > instances . insert ( instance - > owner ) ;
}
2022-07-28 17:41:47 +02:00
instance - > connect_event_signals ( ) ;
2017-10-02 23:24:00 +02:00
}
2021-09-12 20:21:15 +02:00
CSharpInstance * CSharpInstance : : create_for_managed_type ( Object * p_owner , CSharpScript * p_script , const MonoGCHandleData & p_gchandle ) {
CSharpInstance * instance = memnew ( CSharpInstance ( Ref < CSharpScript > ( p_script ) ) ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( p_owner ) ;
2020-01-16 17:11:13 +01:00
2021-09-12 20:21:15 +02:00
instance - > base_ref_counted = rc ! = nullptr ;
instance - > owner = p_owner ;
instance - > gchandle = p_gchandle ;
2018-01-04 21:05:46 +01:00
2021-09-12 20:21:15 +02:00
if ( instance - > base_ref_counted ) {
instance - > _reference_owner_unsafe ( ) ;
}
2017-10-02 23:24:00 +02:00
2023-03-21 17:14:29 +01:00
{
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > get_script_instances_mutex ( ) ) ;
p_script - > instances . insert ( p_owner ) ;
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
return instance ;
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
Object * CSharpInstance : : get_owner ( ) {
return owner ;
}
2018-01-04 21:05:46 +01:00
2021-09-12 20:21:15 +02:00
bool CSharpInstance : : set ( const StringName & p_name , const Variant & p_value ) {
ERR_FAIL_COND_V ( ! script . is_valid ( ) , false ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:23:05 +02:00
return GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Set (
gchandle . get_intptr ( ) , & p_name , & p_value ) ;
2021-09-12 20:21:15 +02:00
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
bool CSharpInstance : : get ( const StringName & p_name , Variant & r_ret ) const {
ERR_FAIL_COND_V ( ! script . is_valid ( ) , false ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
Variant ret_value ;
2018-01-30 18:53:00 +01:00
2021-09-12 20:23:05 +02:00
bool ret = GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Get (
gchandle . get_intptr ( ) , & p_name , & ret_value ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:23:05 +02:00
if ( ret ) {
2021-09-12 20:21:15 +02:00
r_ret = ret_value ;
return true ;
2017-10-02 23:24:00 +02:00
}
return false ;
}
void CSharpInstance : : get_property_list ( List < PropertyInfo > * p_properties ) const {
2021-10-30 16:55:21 +02:00
List < PropertyInfo > props ;
2022-07-31 11:07:48 +03:00
script - > get_script_property_list ( & props ) ;
2019-03-28 11:01:43 +00:00
// Call _get_property_list
ERR_FAIL_COND ( ! script . is_valid ( ) ) ;
2021-09-12 20:21:15 +02:00
StringName method = SNAME ( " _get_property_list " ) ;
2019-03-28 11:01:43 +00:00
2021-09-12 20:21:15 +02:00
Variant ret ;
Callable : : CallError call_error ;
2021-09-12 20:23:05 +02:00
bool ok = GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Call (
gchandle . get_intptr ( ) , & method , nullptr , 0 , & call_error , & ret ) ;
// CALL_ERROR_INVALID_METHOD would simply mean it was not overridden
if ( call_error . error ! = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ) {
if ( call_error . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( " Error calling '_get_property_list': " + Variant : : get_call_error_text ( method , nullptr , 0 , call_error ) ) ;
} else if ( ! ok ) {
ERR_PRINT ( " Unexpected error calling '_get_property_list' " ) ;
} else {
Array array = ret ;
for ( int i = 0 , size = array . size ( ) ; i < size ; i + + ) {
p_properties - > push_back ( PropertyInfo : : from_dict ( array . get ( i ) ) ) ;
}
}
2019-03-28 11:01:43 +00:00
}
2021-10-30 16:55:21 +02:00
2023-04-07 14:01:57 +02:00
for ( PropertyInfo & prop : props ) {
validate_property ( prop ) ;
2021-10-30 16:55:21 +02:00
p_properties - > push_back ( prop ) ;
}
2017-10-02 23:24:00 +02:00
}
Variant : : Type CSharpInstance : : get_property_type ( const StringName & p_name , bool * r_is_valid ) const {
if ( script - > member_info . has ( p_name ) ) {
2020-07-05 19:19:36 +02:00
if ( r_is_valid ) {
2017-10-02 23:24:00 +02:00
* r_is_valid = true ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
return script - > member_info [ p_name ] . type ;
}
2020-07-05 19:19:36 +02:00
if ( r_is_valid ) {
2017-10-02 23:24:00 +02:00
* r_is_valid = false ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
return Variant : : NIL ;
}
2022-08-12 21:43:14 +03:00
bool CSharpInstance : : property_can_revert ( const StringName & p_name ) const {
ERR_FAIL_COND_V ( ! script . is_valid ( ) , false ) ;
2021-09-12 20:21:15 +02:00
Variant name_arg = p_name ;
const Variant * args [ 1 ] = { & name_arg } ;
2022-08-12 21:43:14 +03:00
2021-09-12 20:21:15 +02:00
Variant ret ;
2021-09-12 20:23:05 +02:00
Callable : : CallError call_error ;
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Call (
2023-08-28 13:55:15 +02:00
gchandle . get_intptr ( ) , & SNAME ( " _property_can_revert " ) , args , 1 , & call_error , & ret ) ;
2022-08-12 21:43:14 +03:00
2021-09-12 20:21:15 +02:00
if ( call_error . error ! = Callable : : CallError : : CALL_OK ) {
return false ;
2022-08-12 21:43:14 +03:00
}
2021-09-12 20:21:15 +02:00
return ( bool ) ret ;
2022-08-12 21:43:14 +03:00
}
2023-04-07 14:01:57 +02:00
void CSharpInstance : : validate_property ( PropertyInfo & p_property ) const {
ERR_FAIL_COND ( ! script . is_valid ( ) ) ;
Variant property_arg = ( Dictionary ) p_property ;
const Variant * args [ 1 ] = { & property_arg } ;
Variant ret ;
Callable : : CallError call_error ;
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Call (
gchandle . get_intptr ( ) , & SNAME ( " _validate_property " ) , args , 1 , & call_error , & ret ) ;
if ( call_error . error ! = Callable : : CallError : : CALL_OK ) {
return ;
}
p_property = PropertyInfo : : from_dict ( property_arg ) ;
}
2022-08-12 21:43:14 +03:00
bool CSharpInstance : : property_get_revert ( const StringName & p_name , Variant & r_ret ) const {
ERR_FAIL_COND_V ( ! script . is_valid ( ) , false ) ;
2021-09-12 20:21:15 +02:00
Variant name_arg = p_name ;
const Variant * args [ 1 ] = { & name_arg } ;
2022-08-12 21:43:14 +03:00
2021-09-12 20:21:15 +02:00
Variant ret ;
2021-09-12 20:23:05 +02:00
Callable : : CallError call_error ;
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Call (
2023-08-28 13:55:15 +02:00
gchandle . get_intptr ( ) , & SNAME ( " _property_get_revert " ) , args , 1 , & call_error , & ret ) ;
2022-08-12 21:43:14 +03:00
2021-09-12 20:21:15 +02:00
if ( call_error . error ! = Callable : : CallError : : CALL_OK ) {
return false ;
2022-08-12 21:43:14 +03:00
}
2021-09-12 20:21:15 +02:00
r_ret = ret ;
return true ;
2022-08-12 21:43:14 +03:00
}
2021-09-12 19:53:34 +02:00
void CSharpInstance : : get_method_list ( List < MethodInfo > * p_list ) const {
2022-07-28 17:41:48 +02:00
if ( ! script - > is_valid ( ) | | ! script - > valid ) {
2021-09-12 19:53:34 +02:00
return ;
2021-10-22 19:25:01 +02:00
}
2021-09-12 19:53:34 +02:00
2022-07-28 17:41:48 +02:00
const CSharpScript * top = script . ptr ( ) ;
while ( top ! = nullptr ) {
for ( const CSharpScript : : CSharpMethodInfo & E : top - > methods ) {
p_list - > push_back ( E . method_info ) ;
2021-09-12 19:53:34 +02:00
}
2022-07-28 17:41:48 +02:00
top = top - > base_script . ptr ( ) ;
2021-09-12 19:53:34 +02:00
}
}
2017-10-02 23:24:00 +02:00
bool CSharpInstance : : has_method ( const StringName & p_method ) const {
2020-07-05 19:19:36 +02:00
if ( ! script . is_valid ( ) ) {
2017-10-02 23:24:00 +02:00
return false ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
2021-09-12 20:23:05 +02:00
if ( ! GDMonoCache : : godot_api_cache_updated ) {
2021-09-12 20:21:15 +02:00
return false ;
}
2017-10-02 23:24:00 +02:00
2021-12-28 23:25:16 +01:00
return GDMonoCache : : managed_callbacks . CSharpInstanceBridge_HasMethodUnknownParams (
gchandle . get_intptr ( ) , & p_method ) ;
2017-10-02 23:24:00 +02:00
}
2022-03-09 14:58:40 +01:00
Variant CSharpInstance : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2020-02-08 08:35:52 +01:00
ERR_FAIL_COND_V ( ! script . is_valid ( ) , Variant ( ) ) ;
2020-01-16 17:11:13 +01:00
2021-09-12 20:21:15 +02:00
Variant ret ;
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Call (
gchandle . get_intptr ( ) , & p_method , p_args , p_argcount , & r_error , & ret ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
return ret ;
2017-10-02 23:24:00 +02:00
}
2018-09-12 02:41:54 +02:00
bool CSharpInstance : : _reference_owner_unsafe ( ) {
2017-10-02 23:24:00 +02:00
# ifdef DEBUG_ENABLED
2021-06-04 18:03:15 +02:00
CRASH_COND ( ! base_ref_counted ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( owner = = nullptr ) ;
2018-09-12 02:41:54 +02:00
CRASH_COND ( unsafe_referenced ) ; // already referenced
2017-10-02 23:24:00 +02:00
# endif
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
// See: _unreference_owner_unsafe()
// May not me referenced yet, so we must use init_ref() instead of reference()
2021-06-04 18:03:15 +02:00
if ( static_cast < RefCounted * > ( owner ) - > init_ref ( ) ) {
2020-01-13 21:00:07 +01:00
CSharpLanguage : : get_singleton ( ) - > post_unsafe_reference ( owner ) ;
unsafe_referenced = true ;
}
return unsafe_referenced ;
2017-10-02 23:24:00 +02:00
}
2018-09-12 02:41:54 +02:00
bool CSharpInstance : : _unreference_owner_unsafe ( ) {
2017-10-02 23:24:00 +02:00
# ifdef DEBUG_ENABLED
2021-06-04 18:03:15 +02:00
CRASH_COND ( ! base_ref_counted ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( owner = = nullptr ) ;
2017-10-02 23:24:00 +02:00
# endif
2020-07-05 19:19:36 +02:00
if ( ! unsafe_referenced ) {
2018-09-12 02:41:54 +02:00
return false ; // Already unreferenced
2020-07-05 19:19:36 +02:00
}
2018-09-12 02:41:54 +02:00
2018-10-25 17:21:42 +02:00
unsafe_referenced = false ;
2017-10-02 23:24:00 +02:00
// Called from CSharpInstance::mono_object_disposed() or ~CSharpInstance()
// Unsafe refcount decrement. The managed instance also counts as a reference.
// See: _reference_owner_unsafe()
2019-02-03 06:35:22 +01:00
// Destroying the owner here means self destructing, so we defer the owner destruction to the caller.
2020-01-13 21:00:07 +01:00
CSharpLanguage : : get_singleton ( ) - > pre_unsafe_unreference ( owner ) ;
2021-06-04 18:03:15 +02:00
return static_cast < RefCounted * > ( owner ) - > unreference ( ) ;
2017-10-02 23:24:00 +02:00
}
2021-09-12 20:21:15 +02:00
bool CSharpInstance : : _internal_new_managed ( ) {
2018-09-12 02:41:54 +02:00
CSharpLanguage : : get_singleton ( ) - > release_script_gchandle ( gchandle ) ;
2021-09-12 20:21:15 +02:00
ERR_FAIL_NULL_V ( owner , false ) ;
ERR_FAIL_COND_V ( script . is_null ( ) , false ) ;
2021-09-12 20:23:05 +02:00
bool ok = GDMonoCache : : managed_callbacks . ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance (
script . ptr ( ) , owner , nullptr , 0 ) ;
2018-09-12 02:41:54 +02:00
2021-09-12 20:23:05 +02:00
if ( ! ok ) {
2019-02-03 06:35:22 +01:00
// Important to clear this before destroying the script instance here
2018-09-12 02:41:54 +02:00
script = Ref < CSharpScript > ( ) ;
2020-04-02 01:20:12 +02:00
owner = nullptr ;
2019-07-03 09:44:53 +02:00
2021-09-12 20:21:15 +02:00
return false ;
2018-09-12 02:41:54 +02:00
}
2021-09-12 20:21:15 +02:00
CRASH_COND ( gchandle . is_released ( ) ) ;
2018-09-12 02:41:54 +02:00
2021-09-12 20:21:15 +02:00
return true ;
2018-09-12 02:41:54 +02:00
}
2022-02-27 21:57:30 +01:00
void CSharpInstance : : mono_object_disposed ( GCHandleIntPtr p_gchandle_to_free ) {
2020-10-26 06:59:08 +01:00
// Must make sure event signals are not left dangling
2020-03-14 19:20:17 +01:00
disconnect_event_signals ( ) ;
2018-09-12 02:41:54 +02:00
# ifdef DEBUG_ENABLED
2021-06-04 18:03:15 +02:00
CRASH_COND ( base_ref_counted ) ;
2019-12-11 17:08:40 +01:00
CRASH_COND ( gchandle . is_released ( ) ) ;
2018-09-12 02:41:54 +02:00
# endif
2022-02-27 21:57:30 +01:00
CSharpLanguage : : get_singleton ( ) - > release_script_gchandle_thread_safe ( p_gchandle_to_free , gchandle ) ;
2018-09-12 02:41:54 +02:00
}
2022-02-27 21:57:30 +01:00
void CSharpInstance : : mono_object_disposed_baseref ( GCHandleIntPtr p_gchandle_to_free , bool p_is_finalizer , bool & r_delete_owner , bool & r_remove_script_instance ) {
2018-09-12 02:41:54 +02:00
# ifdef DEBUG_ENABLED
2021-06-04 18:03:15 +02:00
CRASH_COND ( ! base_ref_counted ) ;
2019-12-11 17:08:40 +01:00
CRASH_COND ( gchandle . is_released ( ) ) ;
2018-09-12 02:41:54 +02:00
# endif
2019-02-03 06:35:22 +01:00
2020-10-26 06:59:08 +01:00
// Must make sure event signals are not left dangling
disconnect_event_signals ( ) ;
2019-02-03 06:35:22 +01:00
r_remove_script_instance = false ;
2018-09-12 02:41:54 +02:00
if ( _unreference_owner_unsafe ( ) ) {
2019-02-03 06:35:22 +01:00
// Safe to self destruct here with memdelete(owner), but it's deferred to the caller to prevent future mistakes.
r_delete_owner = true ;
2018-09-12 02:41:54 +02:00
} else {
2019-02-03 06:35:22 +01:00
r_delete_owner = false ;
2022-02-27 21:57:30 +01:00
CSharpLanguage : : get_singleton ( ) - > release_script_gchandle_thread_safe ( p_gchandle_to_free , gchandle ) ;
2019-02-03 06:35:22 +01:00
if ( ! p_is_finalizer ) {
// If the native instance is still alive and Dispose() was called
// (instead of the finalizer), then we remove the script instance.
r_remove_script_instance = true ;
2022-05-28 04:56:46 +02:00
// TODO: Last usage of 'is_finalizing_scripts_domain'. It should be replaced with a check to determine if the load context is being unloaded.
2019-02-03 06:35:22 +01:00
} else if ( ! GDMono : : get_singleton ( ) - > is_finalizing_scripts_domain ( ) ) {
// If the native instance is still alive and this is called from the finalizer,
// then it was referenced from another thread before the finalizer could
// unreference and delete it, so we want to keep it.
// GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this'
// could have already been collected. Instead we will create a new managed instance here.
2021-09-12 20:21:15 +02:00
if ( ! _internal_new_managed ( ) ) {
2019-02-03 06:35:22 +01:00
r_remove_script_instance = true ;
}
2018-09-12 02:41:54 +02:00
}
}
2017-10-02 23:24:00 +02:00
}
2022-07-28 17:41:47 +02:00
void CSharpInstance : : connect_event_signals ( ) {
2022-09-03 20:35:31 +02:00
// The script signals list includes the signals declared in base scripts.
for ( CSharpScript : : EventSignalInfo & signal : script - > get_script_event_signals ( ) ) {
String signal_name = signal . name ;
2020-03-14 19:20:17 +01:00
2022-09-03 20:35:31 +02:00
// TODO: Use pooling for ManagedCallable instances.
EventSignalCallable * event_signal_callable = memnew ( EventSignalCallable ( owner , signal_name ) ) ;
2022-07-28 17:41:47 +02:00
2022-09-03 20:35:31 +02:00
Callable callable ( event_signal_callable ) ;
connected_event_signals . push_back ( callable ) ;
owner - > connect ( signal_name , callable ) ;
2022-07-28 17:41:47 +02:00
}
2020-03-14 19:20:17 +01:00
}
void CSharpInstance : : disconnect_event_signals ( ) {
2021-07-15 23:45:57 -04:00
for ( const Callable & callable : connected_event_signals ) {
2021-04-25 16:40:58 +02:00
const EventSignalCallable * event_signal_callable = static_cast < const EventSignalCallable * > ( callable . get_custom ( ) ) ;
2021-03-15 07:08:06 +01:00
owner - > disconnect ( event_signal_callable - > get_signal ( ) , callable ) ;
2020-03-14 19:20:17 +01:00
}
2021-03-15 07:08:06 +01:00
connected_event_signals . clear ( ) ;
2020-03-14 19:20:17 +01:00
}
2017-10-02 23:24:00 +02:00
void CSharpInstance : : refcount_incremented ( ) {
2018-02-22 15:34:08 +01:00
# ifdef DEBUG_ENABLED
2021-06-04 18:03:15 +02:00
CRASH_COND ( ! base_ref_counted ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( owner = = nullptr ) ;
2018-02-22 15:34:08 +01:00
# endif
2017-10-02 23:24:00 +02:00
2021-06-04 18:03:15 +02:00
RefCounted * rc_owner = Object : : cast_to < RefCounted > ( owner ) ;
2017-10-02 23:24:00 +02:00
2022-09-19 15:45:45 +01:00
if ( rc_owner - > get_reference_count ( ) > 1 & & gchandle . is_weak ( ) ) { // The managed side also holds a reference, hence 1 instead of 0
2017-10-02 23:24:00 +02:00
// The reference count was increased after the managed side was the only one referencing our owner.
// This means the owner is being referenced again by the unmanaged side,
// so the owner must hold the managed side alive again to avoid it from being GCed.
// Release the current weak handle and replace it with a strong handle.
2021-09-12 20:21:15 +02:00
GCHandleIntPtr old_gchandle = gchandle . get_intptr ( ) ;
2022-02-27 21:57:30 +01:00
gchandle . handle = { nullptr } ; // No longer owns the handle (released by swap function)
2021-09-12 20:21:15 +02:00
2022-02-27 21:57:30 +01:00
GCHandleIntPtr new_gchandle = { nullptr } ;
2021-09-12 20:21:15 +02:00
bool create_weak = false ;
2021-09-12 20:23:05 +02:00
bool target_alive = GDMonoCache : : managed_callbacks . ScriptManagerBridge_SwapGCHandleForType (
old_gchandle , & new_gchandle , create_weak ) ;
2021-09-12 20:21:15 +02:00
if ( ! target_alive ) {
return ; // Called after the managed side was collected, so nothing to do here
}
gchandle = MonoGCHandleData ( new_gchandle , gdmono : : GCHandleType : : STRONG_HANDLE ) ;
2017-10-02 23:24:00 +02:00
}
}
bool CSharpInstance : : refcount_decremented ( ) {
2018-02-22 15:34:08 +01:00
# ifdef DEBUG_ENABLED
2021-06-04 18:03:15 +02:00
CRASH_COND ( ! base_ref_counted ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( owner = = nullptr ) ;
2018-02-22 15:34:08 +01:00
# endif
2017-10-02 23:24:00 +02:00
2021-06-04 18:03:15 +02:00
RefCounted * rc_owner = Object : : cast_to < RefCounted > ( owner ) ;
2017-10-02 23:24:00 +02:00
2022-09-19 15:45:45 +01:00
int refcount = rc_owner - > get_reference_count ( ) ;
2017-10-02 23:24:00 +02:00
2019-12-11 17:08:40 +01:00
if ( refcount = = 1 & & ! gchandle . is_weak ( ) ) { // The managed side also holds a reference, hence 1 instead of 0
2017-10-02 23:24:00 +02:00
// If owner owner is no longer referenced by the unmanaged side,
// the managed instance takes responsibility of deleting the owner when GCed.
// Release the current strong handle and replace it with a weak handle.
2021-09-12 20:21:15 +02:00
GCHandleIntPtr old_gchandle = gchandle . get_intptr ( ) ;
2022-02-27 21:57:30 +01:00
gchandle . handle = { nullptr } ; // No longer owns the handle (released by swap function)
2021-09-12 20:21:15 +02:00
2022-02-27 21:57:30 +01:00
GCHandleIntPtr new_gchandle = { nullptr } ;
2021-09-12 20:21:15 +02:00
bool create_weak = true ;
2021-09-12 20:23:05 +02:00
bool target_alive = GDMonoCache : : managed_callbacks . ScriptManagerBridge_SwapGCHandleForType (
old_gchandle , & new_gchandle , create_weak ) ;
2021-09-12 20:21:15 +02:00
if ( ! target_alive ) {
return refcount = = 0 ; // Called after the managed side was collected, so nothing to do here
}
gchandle = MonoGCHandleData ( new_gchandle , gdmono : : GCHandleType : : WEAK_HANDLE ) ;
2017-10-02 23:24:00 +02:00
return false ;
}
ref_dying = ( refcount = = 0 ) ;
return ref_dying ;
}
2022-07-12 23:12:42 +02:00
const Variant CSharpInstance : : get_rpc_config ( ) const {
return script - > get_rpc_config ( ) ;
2020-02-12 11:51:50 +01:00
}
2018-01-04 21:05:46 +01:00
2023-06-24 03:07:22 +02:00
void CSharpInstance : : notification ( int p_notification , bool p_reversed ) {
2017-12-31 22:37:57 +01:00
if ( p_notification = = Object : : NOTIFICATION_PREDELETE ) {
2023-10-20 13:43:42 +02:00
if ( base_ref_counted ) {
// At this point, Dispose() was already called (manually or from the finalizer).
// The RefCounted wouldn't have reached 0 otherwise, since the managed side
// references it and Dispose() needs to be called to release it.
// However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but
// this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784
return ;
}
} else if ( p_notification = = Object : : NOTIFICATION_PREDELETE_CLEANUP ) {
// When NOTIFICATION_PREDELETE_CLEANUP is sent, we also take the chance to call Dispose().
// It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE_CLEANUP is guaranteed
2018-09-12 02:41:54 +02:00
// to be sent at least once, which happens right before the call to the destructor.
2018-11-30 20:43:06 +01:00
predelete_notified = true ;
2021-06-04 18:03:15 +02:00
if ( base_ref_counted ) {
2023-10-20 13:43:42 +02:00
// At this point, Dispose() was already called (manually or from the finalizer).
// The RefCounted wouldn't have reached 0 otherwise, since the managed side
// references it and Dispose() needs to be called to release it.
2018-09-12 02:41:54 +02:00
return ;
2017-12-31 22:37:57 +01:00
}
2018-09-12 02:41:54 +02:00
2023-10-20 13:43:42 +02:00
// NOTIFICATION_PREDELETE_CLEANUP is not sent to scripts.
// After calling Dispose() the C# instance can no longer be used,
// so it should be the last thing we do.
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_CallDispose (
gchandle . get_intptr ( ) , /* okIfNull */ false ) ;
2018-09-12 02:41:54 +02:00
2017-12-31 22:37:57 +01:00
return ;
}
2023-06-24 03:07:22 +02:00
_call_notification ( p_notification , p_reversed ) ;
2017-12-31 22:37:57 +01:00
}
2023-06-24 03:07:22 +02:00
void CSharpInstance : : _call_notification ( int p_notification , bool p_reversed ) {
2021-09-12 20:21:15 +02:00
Variant arg = p_notification ;
const Variant * args [ 1 ] = { & arg } ;
2018-09-12 02:41:54 +02:00
2021-09-12 20:21:15 +02:00
Variant ret ;
2023-08-28 13:55:15 +02:00
Callable : : CallError call_error ;
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_Call (
2023-08-28 13:55:15 +02:00
gchandle . get_intptr ( ) , & SNAME ( " _notification " ) , args , 1 , & call_error , & ret ) ;
2017-10-02 23:24:00 +02:00
}
2019-05-24 00:40:16 +02:00
String CSharpInstance : : to_string ( bool * r_valid ) {
2021-09-12 20:21:15 +02:00
String res ;
bool valid ;
2019-05-24 00:40:16 +02:00
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_CallToString (
gchandle . get_intptr ( ) , & res , & valid ) ;
2019-05-24 00:40:16 +02:00
2021-09-12 20:21:15 +02:00
if ( r_valid ) {
* r_valid = valid ;
2019-05-24 00:40:16 +02:00
}
2021-09-12 20:21:15 +02:00
return res ;
2019-05-24 00:40:16 +02:00
}
2017-10-02 23:24:00 +02:00
Ref < Script > CSharpInstance : : get_script ( ) const {
return script ;
}
ScriptLanguage * CSharpInstance : : get_language ( ) {
return CSharpLanguage : : get_singleton ( ) ;
}
2020-03-14 19:20:17 +01:00
CSharpInstance : : CSharpInstance ( const Ref < CSharpScript > & p_script ) :
script ( p_script ) {
2017-10-02 23:24:00 +02:00
}
CSharpInstance : : ~ CSharpInstance ( ) {
2019-02-03 06:35:22 +01:00
destructing_script_instance = true ;
2020-10-26 06:59:08 +01:00
// Must make sure event signals are not left dangling
disconnect_event_signals ( ) ;
2019-12-11 17:08:40 +01:00
if ( ! gchandle . is_released ( ) ) {
2018-11-30 20:43:06 +01:00
if ( ! predelete_notified & & ! ref_dying ) {
// This destructor is not called from the owners destructor.
// This could be being called from the owner's set_script_instance method,
// meaning this script is being replaced with another one. If this is the case,
2020-04-02 01:20:12 +02:00
// we must call Dispose here, because Dispose calls owner->set_script_instance(nullptr)
2018-11-30 20:43:06 +01:00
// and that would mess up with the new script instance if called later.
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_CallDispose (
gchandle . get_intptr ( ) , /* okIfNull */ true ) ;
2018-11-30 20:43:06 +01:00
}
2019-12-11 17:08:40 +01:00
gchandle . release ( ) ; // Make sure the gchandle is released
2017-10-02 23:24:00 +02:00
}
2019-02-03 06:35:22 +01:00
// If not being called from the owner's destructor, and we still hold a reference to the owner
2021-06-04 18:03:15 +02:00
if ( base_ref_counted & & ! ref_dying & & owner & & unsafe_referenced ) {
2019-02-03 06:35:22 +01:00
// The owner's script or script instance is being replaced (or removed)
// Transfer ownership to an "instance binding"
2021-06-04 18:03:15 +02:00
RefCounted * rc_owner = static_cast < RefCounted * > ( owner ) ;
2020-01-15 23:46:42 +01:00
// We will unreference the owner before referencing it again, so we need to keep it alive
2021-06-04 18:03:15 +02:00
Ref < RefCounted > scope_keep_owner_alive ( rc_owner ) ;
2020-01-15 23:46:42 +01:00
( void ) scope_keep_owner_alive ;
// 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 ( ) ;
2020-07-13 14:13:38 -04:00
CRASH_COND ( die ) ; // `owner_keep_alive` holds a reference, so it can't die
2021-07-08 16:16:02 -03:00
2021-08-16 17:16:36 +02:00
void * data = CSharpLanguage : : get_instance_binding ( owner ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( data = = nullptr ) ;
2022-05-13 15:04:37 +02:00
CSharpScriptBinding & script_binding = ( ( RBMap < Object * , CSharpScriptBinding > : : Element * ) data ) - > get ( ) ;
2021-08-16 17:16:36 +02:00
CRASH_COND ( ! script_binding . inited ) ;
2019-02-03 06:35:22 +01:00
2020-01-15 23:46:42 +01:00
# ifdef DEBUG_ENABLED
// The "instance binding" holds a reference so the refcount should be at least 2 before `scope_keep_owner_alive` goes out of scope
2022-09-19 15:45:45 +01:00
CRASH_COND ( rc_owner - > get_reference_count ( ) < = 1 ) ;
2020-01-15 23:46:42 +01:00
# endif
2017-10-02 23:24:00 +02:00
}
if ( script . is_valid ( ) & & owner ) {
2020-02-26 11:28:13 +01:00
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > script_instances_mutex ) ;
2017-10-02 23:24:00 +02:00
# ifdef DEBUG_ENABLED
// CSharpInstance must not be created unless it's going to be added to the list for sure
2022-05-19 17:00:06 +02:00
HashSet < Object * > : : Iterator match = script - > instances . find ( owner ) ;
2017-10-02 23:24:00 +02:00
CRASH_COND ( ! match ) ;
2022-05-19 17:00:06 +02:00
script - > instances . remove ( match ) ;
2017-10-02 23:24:00 +02:00
# else
script - > instances . erase ( owner ) ;
# endif
}
}
# ifdef TOOLS_ENABLED
void CSharpScript : : _placeholder_erased ( PlaceHolderScriptInstance * p_placeholder ) {
placeholders . erase ( p_placeholder ) ;
}
# endif
# ifdef TOOLS_ENABLED
2022-05-13 15:04:37 +02:00
void CSharpScript : : _update_exports_values ( HashMap < StringName , Variant > & values , List < PropertyInfo > & propnames ) {
2021-07-26 21:31:17 +02:00
for ( const KeyValue < StringName , Variant > & E : exported_members_defval_cache ) {
values [ E . key ] = E . value ;
2017-10-02 23:24:00 +02:00
}
2021-07-26 21:31:17 +02:00
for ( const PropertyInfo & prop_info : exported_members_cache ) {
propnames . push_back ( prop_info ) ;
2017-10-02 23:24:00 +02:00
}
2022-07-31 11:07:48 +03:00
2022-05-28 04:56:46 +02:00
if ( base_script . is_valid ( ) ) {
base_script - > _update_exports_values ( values , propnames ) ;
2022-07-31 11:07:48 +03:00
}
2017-10-02 23:24:00 +02:00
}
2019-07-08 15:22:51 +02:00
# endif
2019-07-03 09:44:53 +02:00
2022-09-08 20:13:49 +02:00
void GD_CLR_STDCALL CSharpScript : : _add_property_info_list_callback ( CSharpScript * p_script , const String * p_current_class_name , void * p_props , int32_t p_count ) {
GDMonoCache : : godotsharp_property_info * props = ( GDMonoCache : : godotsharp_property_info * ) p_props ;
# ifdef TOOLS_ENABLED
p_script - > exported_members_cache . push_back ( PropertyInfo (
Variant : : NIL , * p_current_class_name , PROPERTY_HINT_NONE ,
p_script - > get_path ( ) , PROPERTY_USAGE_CATEGORY ) ) ;
# endif
for ( int i = 0 ; i < p_count ; i + + ) {
const GDMonoCache : : godotsharp_property_info & prop = props [ i ] ;
StringName name = * reinterpret_cast < const StringName * > ( & prop . name ) ;
String hint_string = * reinterpret_cast < const String * > ( & prop . hint_string ) ;
PropertyInfo pinfo ( prop . type , name , prop . hint , hint_string , prop . usage ) ;
p_script - > member_info [ name ] = pinfo ;
if ( prop . exported ) {
# ifdef TOOLS_ENABLED
p_script - > exported_members_cache . push_back ( pinfo ) ;
# endif
# if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
p_script - > exported_members_names . insert ( name ) ;
# endif
}
}
}
# ifdef TOOLS_ENABLED
void GD_CLR_STDCALL CSharpScript : : _add_property_default_values_callback ( CSharpScript * p_script , void * p_def_vals , int32_t p_count ) {
GDMonoCache : : godotsharp_property_def_val_pair * def_vals = ( GDMonoCache : : godotsharp_property_def_val_pair * ) p_def_vals ;
for ( int i = 0 ; i < p_count ; i + + ) {
const GDMonoCache : : godotsharp_property_def_val_pair & def_val_pair = def_vals [ i ] ;
StringName name = * reinterpret_cast < const StringName * > ( & def_val_pair . name ) ;
Variant value = * reinterpret_cast < const Variant * > ( & def_val_pair . value ) ;
p_script - > exported_members_defval_cache [ name ] = value ;
}
}
# endif
2021-06-13 13:32:44 +02:00
bool CSharpScript : : _update_exports ( PlaceHolderScriptInstance * p_instance_to_update ) {
2017-10-02 23:24:00 +02:00
# ifdef TOOLS_ENABLED
2020-05-11 03:20:11 +09:00
bool is_editor = Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
2020-07-05 19:19:36 +02:00
if ( is_editor ) {
2020-05-11 03:20:11 +09:00
placeholder_fallback_enabled = true ; // until proven otherwise
2020-07-05 19:19:36 +02:00
}
2020-05-11 03:20:11 +09:00
# endif
2020-07-05 19:19:36 +02:00
if ( ! valid ) {
2017-10-02 23:24:00 +02:00
return false ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
bool changed = false ;
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
if ( exports_invalidated )
# endif
{
2022-02-27 21:57:52 +01:00
# ifdef TOOLS_ENABLED
2022-02-27 21:57:30 +01:00
exports_invalidated = false ;
2022-02-27 21:57:52 +01:00
# endif
2020-01-16 17:11:13 +01:00
2017-10-02 23:24:00 +02:00
changed = true ;
member_info . clear ( ) ;
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2022-02-27 21:57:30 +01:00
exported_members_cache . clear ( ) ;
exported_members_defval_cache . clear ( ) ;
2020-05-11 03:20:11 +09:00
# endif
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:30 +01:00
if ( GDMonoCache : : godot_api_cache_updated ) {
2022-09-08 20:13:49 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_GetPropertyInfoList ( this , & _add_property_info_list_callback ) ;
2020-05-22 00:58:34 +02:00
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2022-09-08 20:13:49 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_GetPropertyDefaultValues ( this , & _add_property_default_values_callback ) ;
2022-02-27 21:57:52 +01:00
# endif
2020-01-13 20:58:46 +01:00
}
2017-10-02 23:24:00 +02:00
}
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
if ( is_editor ) {
placeholder_fallback_enabled = false ;
2019-01-10 00:26:00 +01:00
2021-06-13 13:32:44 +02:00
if ( ( changed | | p_instance_to_update ) & & placeholders . size ( ) ) {
2020-05-11 03:20:11 +09:00
// Update placeholders if any
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > values ;
2020-05-11 03:20:11 +09:00
List < PropertyInfo > propnames ;
_update_exports_values ( values , propnames ) ;
2017-10-02 23:24:00 +02:00
2021-06-13 13:32:44 +02:00
if ( changed ) {
2023-03-24 12:38:00 +01:00
for ( PlaceHolderScriptInstance * instance : placeholders ) {
instance - > update ( propnames , values ) ;
2021-06-13 13:32:44 +02:00
}
} else {
p_instance_to_update - > update ( propnames , values ) ;
2020-05-11 03:20:11 +09:00
}
2017-10-02 23:24:00 +02:00
}
}
2020-05-11 03:20:11 +09:00
# endif
2017-10-02 23:24:00 +02:00
return changed ;
}
2017-10-30 22:17:20 +01:00
bool CSharpScript : : _get ( const StringName & p_name , Variant & r_ret ) const {
2023-08-28 13:55:15 +02:00
if ( p_name = = SNAME ( " script/source " ) ) {
2017-10-30 22:17:20 +01:00
r_ret = get_source_code ( ) ;
return true ;
}
return false ;
}
bool CSharpScript : : _set ( const StringName & p_name , const Variant & p_value ) {
2023-08-28 13:55:15 +02:00
if ( p_name = = SNAME ( " script/source " ) ) {
2017-10-30 22:17:20 +01:00
set_source_code ( p_value ) ;
reload ( ) ;
return true ;
}
return false ;
}
void CSharpScript : : _get_property_list ( List < PropertyInfo > * p_properties ) const {
2023-08-28 13:55:15 +02:00
p_properties - > push_back ( PropertyInfo ( Variant : : STRING , SNAME ( " script/source " ) , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL ) ) ;
2017-10-30 22:17:20 +01:00
}
2017-10-02 23:24:00 +02:00
void CSharpScript : : _bind_methods ( ) {
2019-08-26 16:36:51 +00:00
ClassDB : : bind_vararg_method ( METHOD_FLAGS_DEFAULT , " new " , & CSharpScript : : _new , MethodInfo ( " new " ) ) ;
2017-10-02 23:24:00 +02:00
}
2022-02-27 21:57:36 +01:00
void CSharpScript : : reload_registered_script ( Ref < CSharpScript > p_script ) {
2021-09-12 20:21:15 +02:00
// IMPORTANT:
// This method must be called only after the CSharpScript and its associated type
// have been added to the script bridge map in the ScriptManagerBridge C# class.
2022-02-27 21:57:36 +01:00
// Other than that, it's the same as `CSharpScript::reload`.
2019-07-08 15:18:38 +02:00
2022-02-27 21:57:36 +01:00
// This method should not fail, only assertions allowed.
// Unlike `reload`, we print an error rather than silently returning,
// as we can assert this won't be called a second time until invalidated.
ERR_FAIL_COND ( ! p_script - > reload_invalidated ) ;
2019-07-08 15:18:38 +02:00
2020-10-12 09:48:29 +02:00
p_script - > valid = true ;
2021-12-06 19:45:59 +01:00
p_script - > reload_invalidated = false ;
2020-10-12 09:48:29 +02:00
update_script_class_info ( p_script ) ;
2022-02-27 21:57:30 +01:00
p_script - > _update_exports ( ) ;
2023-02-02 00:54:16 +01:00
# if TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem * efs = EditorFileSystem : : get_singleton ( ) ;
2023-07-07 20:51:46 +02:00
if ( efs & & ! p_script - > get_path ( ) . is_empty ( ) ) {
2023-02-02 00:54:16 +01:00
efs - > update_file ( p_script - > get_path ( ) ) ;
}
# endif
2020-10-12 09:48:29 +02:00
}
// Extract information about the script using the mono class.
void CSharpScript : : update_script_class_info ( Ref < CSharpScript > p_script ) {
2021-09-12 20:21:15 +02:00
bool tool = false ;
2023-02-02 00:54:16 +01:00
bool global_class = false ;
2023-05-23 09:25:34 -07:00
bool abstract_class = false ;
2022-07-28 17:41:47 +02:00
2022-12-07 12:11:28 +01:00
// TODO: Use GDExtension godot_dictionary
2022-07-28 17:41:48 +02:00
Array methods_array ;
methods_array . ~ Array ( ) ;
2021-09-12 20:21:15 +02:00
Dictionary rpc_functions_dict ;
rpc_functions_dict . ~ Dictionary ( ) ;
2022-07-28 17:41:47 +02:00
Dictionary signals_dict ;
signals_dict . ~ Dictionary ( ) ;
2023-02-02 00:54:16 +01:00
String class_name ;
String icon_path ;
2022-05-28 04:56:46 +02:00
Ref < CSharpScript > base_script ;
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_UpdateScriptClassInfo (
2023-05-23 09:25:34 -07:00
p_script . ptr ( ) , & class_name , & tool , & global_class , & abstract_class , & icon_path ,
2023-02-02 00:54:16 +01:00
& methods_array , & rpc_functions_dict , & signals_dict , & base_script ) ;
2017-10-02 23:24:00 +02:00
2023-02-02 00:54:16 +01:00
p_script - > class_name = class_name ;
2021-09-12 20:21:15 +02:00
p_script - > tool = tool ;
2023-02-02 00:54:16 +01:00
p_script - > global_class = global_class ;
2023-05-23 09:25:34 -07:00
p_script - > abstract_class = abstract_class ;
2023-02-02 00:54:16 +01:00
p_script - > icon_path = icon_path ;
2017-10-02 23:24:00 +02:00
2022-07-12 23:12:42 +02:00
p_script - > rpc_config . clear ( ) ;
2021-09-12 20:21:15 +02:00
p_script - > rpc_config = rpc_functions_dict ;
2022-05-28 04:56:46 +02:00
2022-07-28 17:41:48 +02:00
// Methods
p_script - > methods . clear ( ) ;
p_script - > methods . resize ( methods_array . size ( ) ) ;
int push_index = 0 ;
for ( int i = 0 ; i < methods_array . size ( ) ; i + + ) {
Dictionary method_info_dict = methods_array [ i ] ;
StringName name = method_info_dict [ " name " ] ;
MethodInfo mi ;
mi . name = name ;
Array params = method_info_dict [ " params " ] ;
for ( int j = 0 ; j < params . size ( ) ; j + + ) {
Dictionary param = params [ j ] ;
Variant : : Type param_type = ( Variant : : Type ) ( int ) param [ " type " ] ;
PropertyInfo arg_info = PropertyInfo ( param_type , ( String ) param [ " name " ] ) ;
arg_info . usage = ( uint32_t ) param [ " usage " ] ;
2023-06-16 23:05:11 +02:00
if ( param . has ( " class_name " ) ) {
arg_info . class_name = ( StringName ) param [ " class_name " ] ;
}
2022-07-28 17:41:48 +02:00
mi . arguments . push_back ( arg_info ) ;
}
2023-09-20 22:49:33 +08:00
mi . flags = ( uint32_t ) method_info_dict [ " flags " ] ;
2022-07-28 17:41:48 +02:00
p_script - > methods . set ( push_index + + , CSharpMethodInfo { name , mi } ) ;
}
2022-07-28 17:41:47 +02:00
// Event signals
// Performance is not critical here as this will be replaced with source generators.
p_script - > event_signals . clear ( ) ;
// Sigh... can't we just have capacity?
p_script - > event_signals . resize ( signals_dict . size ( ) ) ;
2022-07-28 17:41:48 +02:00
push_index = 0 ;
2022-07-28 17:41:47 +02:00
for ( const Variant * s = signals_dict . next ( nullptr ) ; s ! = nullptr ; s = signals_dict . next ( s ) ) {
StringName name = * s ;
MethodInfo mi ;
mi . name = name ;
Array params = signals_dict [ * s ] ;
for ( int i = 0 ; i < params . size ( ) ; i + + ) {
Dictionary param = params [ i ] ;
Variant : : Type param_type = ( Variant : : Type ) ( int ) param [ " type " ] ;
PropertyInfo arg_info = PropertyInfo ( param_type , ( String ) param [ " name " ] ) ;
arg_info . usage = ( uint32_t ) param [ " usage " ] ;
2023-06-16 23:05:11 +02:00
if ( param . has ( " class_name " ) ) {
arg_info . class_name = ( StringName ) param [ " class_name " ] ;
}
2022-07-28 17:41:47 +02:00
mi . arguments . push_back ( arg_info ) ;
}
p_script - > event_signals . set ( push_index + + , EventSignalInfo { name , mi } ) ;
}
2022-05-28 04:56:46 +02:00
p_script - > base_script = base_script ;
2017-10-02 23:24:00 +02:00
}
2021-06-17 16:03:09 -06:00
bool CSharpScript : : can_instantiate ( ) const {
2018-07-29 22:40:09 +02:00
# ifdef TOOLS_ENABLED
2018-10-22 19:43:19 +02:00
bool extra_cond = tool | | ScriptServer : : is_scripting_enabled ( ) ;
2018-07-29 22:40:09 +02:00
# else
2018-10-22 19:43:19 +02:00
bool extra_cond = true ;
2018-07-29 22:40:09 +02:00
# endif
2018-10-22 19:43:19 +02:00
// FIXME Need to think this through better.
// For tool scripts, this will never fire if the class is not found. That's because we
// don't know if it's a tool script if we can't find the class to access the attributes.
2021-09-12 20:21:15 +02:00
if ( extra_cond & & ! valid ) {
2022-12-17 19:59:56 +01:00
ERR_FAIL_V_MSG ( false , " Cannot instance script because the associated class could not be found. Script: ' " + get_path ( ) + " '. Make sure the script exists and contains a class definition with a name that matches the filename of the script exactly (it's case-sensitive). " ) ;
2018-10-22 19:43:19 +02:00
}
2023-05-23 09:25:34 -07:00
return valid & & ! abstract_class & & extra_cond ;
2017-10-02 23:24:00 +02:00
}
StringName CSharpScript : : get_instance_base_type ( ) const {
2021-09-12 20:21:15 +02:00
StringName native_name ;
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_GetScriptNativeName ( this , & native_name ) ;
2021-09-12 20:21:15 +02:00
return native_name ;
2017-10-02 23:24:00 +02:00
}
2021-06-04 18:03:15 +02:00
CSharpInstance * CSharpScript : : _create_instance ( const Variant * * p_args , int p_argcount , Object * p_owner , bool p_is_ref_counted , Callable : : CallError & r_error ) {
2017-10-02 23:24:00 +02:00
/* STEP 1, CREATE */
2019-02-28 23:12:49 +01:00
2021-06-04 18:03:15 +02:00
Ref < RefCounted > ref ;
if ( p_is_ref_counted ) {
2019-02-03 06:35:22 +01:00
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
2021-06-04 18:03:15 +02:00
ref = Ref < RefCounted > ( static_cast < RefCounted * > ( p_owner ) ) ;
2019-02-03 06:35:22 +01:00
}
2021-08-16 17:16:36 +02:00
2019-02-03 06:35:22 +01:00
// If the object had a script instance binding, dispose it before adding the CSharpInstance
2021-08-16 17:16:36 +02:00
if ( CSharpLanguage : : has_instance_binding ( p_owner ) ) {
void * data = CSharpLanguage : : get_existing_instance_binding ( p_owner ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( data = = nullptr ) ;
2019-02-03 06:35:22 +01:00
2022-05-13 15:04:37 +02:00
CSharpScriptBinding & script_binding = ( ( RBMap < Object * , CSharpScriptBinding > : : Element * ) data ) - > get ( ) ;
2019-12-11 17:08:40 +01:00
if ( script_binding . inited & & ! script_binding . gchandle . is_released ( ) ) {
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . CSharpInstanceBridge_CallDispose (
script_binding . gchandle . get_intptr ( ) , /* okIfNull */ true ) ;
2019-02-03 06:35:22 +01:00
2019-12-11 17:08:40 +01:00
script_binding . gchandle . release ( ) ; // Just in case
2019-02-03 06:35:22 +01:00
script_binding . inited = false ;
}
}
2021-08-16 17:16:36 +02:00
2020-03-14 19:20:17 +01:00
CSharpInstance * instance = memnew ( CSharpInstance ( Ref < CSharpScript > ( this ) ) ) ;
2021-06-04 18:03:15 +02:00
instance - > base_ref_counted = p_is_ref_counted ;
2017-10-02 23:24:00 +02:00
instance - > owner = p_owner ;
instance - > owner - > set_script_instance ( instance ) ;
/* STEP 2, INITIALIZE AND CONSTRUCT */
2021-09-12 20:23:05 +02:00
bool ok = GDMonoCache : : managed_callbacks . ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance (
this , p_owner , p_args , p_argcount ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:23:05 +02:00
if ( ! ok ) {
2019-02-03 06:35:22 +01:00
// Important to clear this before destroying the script instance here
2017-10-02 23:24:00 +02:00
instance - > script = Ref < CSharpScript > ( ) ;
2020-04-02 01:20:12 +02:00
instance - > owner = nullptr ;
p_owner - > set_script_instance ( nullptr ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
return nullptr ;
2020-07-05 19:19:36 +02:00
}
2019-02-03 06:35:22 +01:00
2021-09-12 20:21:15 +02:00
CRASH_COND ( instance - > gchandle . is_released ( ) ) ;
2017-10-02 23:24:00 +02:00
/* STEP 3, PARTY */
//@TODO make thread safe
return instance ;
}
2020-02-19 16:27:19 -03:00
Variant CSharpScript : : _new ( const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2017-10-02 23:24:00 +02:00
if ( ! valid ) {
2020-02-19 16:27:19 -03:00
r_error . error = Callable : : CallError : : CALL_ERROR_INVALID_METHOD ;
2017-10-02 23:24:00 +02:00
return Variant ( ) ;
}
2020-02-19 16:27:19 -03:00
r_error . error = Callable : : CallError : : CALL_OK ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
StringName native_name ;
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_GetScriptNativeName ( this , & native_name ) ;
2021-09-12 20:21:15 +02:00
ERR_FAIL_COND_V ( native_name = = StringName ( ) , Variant ( ) ) ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
Object * owner = ClassDB : : instantiate ( native_name ) ;
2017-10-02 23:24:00 +02:00
2022-05-03 01:43:50 +02:00
Ref < RefCounted > ref ;
2021-06-04 18:03:15 +02:00
RefCounted * r = Object : : cast_to < RefCounted > ( owner ) ;
2017-10-02 23:24:00 +02:00
if ( r ) {
2022-05-03 01:43:50 +02:00
ref = Ref < RefCounted > ( r ) ;
2017-10-02 23:24:00 +02:00
}
2020-04-02 01:20:12 +02:00
CSharpInstance * instance = _create_instance ( p_args , p_argcount , owner , r ! = nullptr , r_error ) ;
2017-10-02 23:24:00 +02:00
if ( ! instance ) {
if ( ref . is_null ( ) ) {
2021-08-16 17:16:36 +02:00
memdelete ( owner ) ; // no owner, sorry
2017-10-02 23:24:00 +02:00
}
return Variant ( ) ;
}
if ( ref . is_valid ( ) ) {
return ref ;
} else {
return owner ;
}
}
ScriptInstance * CSharpScript : : instance_create ( Object * p_this ) {
2018-07-29 22:40:09 +02:00
# ifdef DEBUG_ENABLED
CRASH_COND ( ! valid ) ;
2017-10-02 23:24:00 +02:00
# endif
2018-05-26 10:30:36 +02:00
2021-09-12 20:21:15 +02:00
StringName native_name ;
2021-09-12 20:23:05 +02:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_GetScriptNativeName ( this , & native_name ) ;
2021-09-12 20:21:15 +02:00
ERR_FAIL_COND_V ( native_name = = StringName ( ) , nullptr ) ;
if ( ! ClassDB : : is_parent_class ( p_this - > get_class_name ( ) , native_name ) ) {
if ( EngineDebugger : : is_active ( ) ) {
CSharpLanguage : : get_singleton ( ) - > debug_break_parse ( get_path ( ) , 0 ,
" Script inherits from native type ' " + String ( native_name ) +
2022-10-04 12:56:49 +02:00
" ', so it can't be assigned to an object of type: ' " + p_this - > get_class ( ) + " ' " ) ;
2017-10-02 23:24:00 +02:00
}
2022-10-04 12:56:49 +02:00
ERR_FAIL_V_MSG ( nullptr , " Script inherits from native type ' " + String ( native_name ) + " ', so it can't be assigned to an object of type: ' " + p_this - > get_class ( ) + " '. " ) ;
2017-10-02 23:24:00 +02:00
}
2020-02-19 16:27:19 -03:00
Callable : : CallError unchecked_error ;
2021-06-04 18:03:15 +02:00
return _create_instance ( nullptr , 0 , p_this , Object : : cast_to < RefCounted > ( p_this ) ! = nullptr , unchecked_error ) ;
2017-10-02 23:24:00 +02:00
}
2018-07-29 22:40:09 +02:00
PlaceHolderScriptInstance * CSharpScript : : placeholder_instance_create ( Object * p_this ) {
# ifdef TOOLS_ENABLED
PlaceHolderScriptInstance * si = memnew ( PlaceHolderScriptInstance ( CSharpLanguage : : get_singleton ( ) , Ref < Script > ( this ) , p_this ) ) ;
placeholders . insert ( si ) ;
2021-06-13 13:32:44 +02:00
_update_exports ( si ) ;
2018-07-29 22:40:09 +02:00
return si ;
# else
2020-04-02 01:20:12 +02:00
return nullptr ;
2018-07-29 22:40:09 +02:00
# endif
}
2017-10-02 23:24:00 +02:00
bool CSharpScript : : instance_has ( const Object * p_this ) const {
2020-02-26 11:28:13 +01:00
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > script_instances_mutex ) ;
2018-11-30 20:43:06 +01:00
return instances . has ( ( Object * ) p_this ) ;
2017-10-02 23:24:00 +02:00
}
bool CSharpScript : : has_source_code ( ) const {
2020-12-15 12:04:21 +00:00
return ! source . is_empty ( ) ;
2017-10-02 23:24:00 +02:00
}
String CSharpScript : : get_source_code ( ) const {
return source ;
}
void CSharpScript : : set_source_code ( const String & p_code ) {
2020-07-05 19:19:36 +02:00
if ( source = = p_code ) {
2017-10-02 23:24:00 +02:00
return ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
source = p_code ;
# ifdef TOOLS_ENABLED
source_changed_cache = true ;
# endif
}
2018-11-01 08:35:16 -07:00
void CSharpScript : : get_script_method_list ( List < MethodInfo > * p_list ) const {
2021-09-12 20:21:15 +02:00
if ( ! valid ) {
2018-11-01 08:35:16 -07:00
return ;
2020-07-05 19:19:36 +02:00
}
2018-11-01 08:35:16 -07:00
2022-07-28 17:41:48 +02:00
const CSharpScript * top = this ;
while ( top ! = nullptr ) {
for ( const CSharpMethodInfo & E : top - > methods ) {
p_list - > push_back ( E . method_info ) ;
2021-09-12 19:53:34 +02:00
}
2022-07-28 17:41:48 +02:00
top = top - > base_script . ptr ( ) ;
2018-11-01 08:35:16 -07:00
}
}
2017-10-02 23:24:00 +02:00
bool CSharpScript : : has_method ( const StringName & p_method ) const {
2022-07-28 17:41:48 +02:00
if ( ! valid ) {
return false ;
}
for ( const CSharpMethodInfo & E : methods ) {
if ( E . name = = p_method ) {
return true ;
}
}
2021-12-28 23:25:16 +01:00
return false ;
2017-10-02 23:24:00 +02:00
}
2018-11-01 08:35:16 -07:00
MethodInfo CSharpScript : : get_method_info ( const StringName & p_method ) const {
2021-09-12 20:21:15 +02:00
if ( ! valid ) {
2018-11-01 08:35:16 -07:00
return MethodInfo ( ) ;
2020-07-05 19:19:36 +02:00
}
2018-11-01 08:35:16 -07:00
2022-07-28 17:41:48 +02:00
for ( const CSharpMethodInfo & E : methods ) {
if ( E . name = = p_method ) {
return E . method_info ;
2018-11-01 08:35:16 -07:00
}
}
return MethodInfo ( ) ;
}
2023-09-20 22:49:33 +08:00
Variant CSharpScript : : callp ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2023-11-14 16:56:16 +01:00
if ( valid ) {
Variant ret ;
bool ok = GDMonoCache : : managed_callbacks . ScriptManagerBridge_CallStatic ( this , & p_method , p_args , p_argcount , & r_error , & ret ) ;
if ( ok ) {
return ret ;
}
2023-09-20 22:49:33 +08:00
}
return Script : : callp ( p_method , p_args , p_argcount , r_error ) ;
}
2017-10-02 23:24:00 +02:00
Error CSharpScript : : reload ( bool p_keep_state ) {
2021-12-06 19:45:59 +01:00
if ( ! reload_invalidated ) {
return OK ;
2018-11-30 20:43:06 +01:00
}
2017-10-02 23:24:00 +02:00
2021-12-06 19:45:59 +01:00
// In the case of C#, reload doesn't really do any script reloading.
// That's done separately via domain reloading.
reload_invalidated = false ;
2017-10-02 23:24:00 +02:00
2021-09-12 20:21:15 +02:00
String script_path = get_path ( ) ;
2017-10-05 00:10:51 +02:00
2021-09-12 20:23:05 +02:00
valid = GDMonoCache : : managed_callbacks . ScriptManagerBridge_AddScriptBridge ( this , & script_path ) ;
2018-04-24 20:42:31 +02:00
2021-09-12 20:21:15 +02:00
if ( valid ) {
2017-10-05 00:10:51 +02:00
# ifdef DEBUG_ENABLED
2021-09-12 20:21:15 +02:00
print_verbose ( " Found class for script " + get_path ( ) ) ;
2017-10-05 00:10:51 +02:00
# endif
2021-03-06 00:12:42 +01:00
update_script_class_info ( this ) ;
2017-10-02 23:24:00 +02:00
2021-03-06 00:12:42 +01:00
_update_exports ( ) ;
2023-02-02 00:54:16 +01:00
# if TOOLS_ENABLED
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem * efs = EditorFileSystem : : get_singleton ( ) ;
if ( efs ) {
efs - > update_file ( script_path ) ;
}
# endif
2017-10-02 23:24:00 +02:00
}
2021-03-06 00:12:42 +01:00
return OK ;
2017-10-02 23:24:00 +02:00
}
ScriptLanguage * CSharpScript : : get_language ( ) const {
return CSharpLanguage : : get_singleton ( ) ;
}
bool CSharpScript : : get_property_default_value ( const StringName & p_property , Variant & r_value ) const {
# ifdef TOOLS_ENABLED
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > : : ConstIterator E = exported_members_defval_cache . find ( p_property ) ;
2017-10-02 23:24:00 +02:00
if ( E ) {
2022-05-13 15:04:37 +02:00
r_value = E - > value ;
2017-10-02 23:24:00 +02:00
return true ;
}
2022-05-28 04:56:46 +02:00
if ( base_script . is_valid ( ) ) {
return base_script - > get_property_default_value ( p_property , r_value ) ;
2017-10-02 23:24:00 +02:00
}
# endif
return false ;
}
void CSharpScript : : update_exports ( ) {
# ifdef TOOLS_ENABLED
_update_exports ( ) ;
# endif
}
2018-01-18 23:27:43 +01:00
bool CSharpScript : : has_script_signal ( const StringName & p_signal ) const {
2021-09-12 20:21:15 +02:00
if ( ! valid ) {
return false ;
}
2021-09-12 20:23:05 +02:00
if ( ! GDMonoCache : : godot_api_cache_updated ) {
2021-09-12 20:21:15 +02:00
return false ;
}
2022-07-28 17:41:47 +02:00
for ( const EventSignalInfo & signal : event_signals ) {
if ( signal . name = = p_signal ) {
return true ;
}
}
2021-09-12 20:21:15 +02:00
2022-07-28 17:41:47 +02:00
return false ;
2018-01-18 23:27:43 +01:00
}
void CSharpScript : : get_script_signal_list ( List < MethodInfo > * r_signals ) const {
2021-09-12 20:21:15 +02:00
if ( ! valid ) {
return ;
}
2020-03-14 19:20:17 +01:00
2022-07-28 17:41:47 +02:00
for ( const EventSignalInfo & signal : get_script_event_signals ( ) ) {
r_signals - > push_back ( signal . method_info ) ;
2021-09-12 20:21:15 +02:00
}
2022-07-28 17:41:47 +02:00
}
2020-03-14 19:20:17 +01:00
2022-07-28 17:41:47 +02:00
Vector < CSharpScript : : EventSignalInfo > CSharpScript : : get_script_event_signals ( ) const {
if ( ! valid ) {
return Vector < EventSignalInfo > ( ) ;
2018-01-18 23:27:43 +01:00
}
2022-07-28 17:41:47 +02:00
return event_signals ;
2018-01-18 23:27:43 +01:00
}
2020-04-20 19:06:00 -03:00
bool CSharpScript : : inherits_script ( const Ref < Script > & p_script ) const {
Ref < CSharpScript > cs = p_script ;
if ( cs . is_null ( ) ) {
return false ;
}
2021-09-12 20:21:15 +02:00
if ( ! valid | | ! cs - > valid ) {
2020-05-10 21:49:15 +02:00
return false ;
}
2021-09-12 20:23:05 +02:00
if ( ! GDMonoCache : : godot_api_cache_updated ) {
2021-09-12 20:21:15 +02:00
return false ;
2020-05-10 21:49:15 +02:00
}
2021-09-12 20:23:05 +02:00
return GDMonoCache : : managed_callbacks . ScriptManagerBridge_ScriptIsOrInherits ( this , cs . ptr ( ) ) ;
2020-04-20 19:06:00 -03:00
}
2017-10-02 23:24:00 +02:00
Ref < Script > CSharpScript : : get_base_script ( ) const {
2023-02-02 00:54:16 +01:00
return base_script . is_valid ( ) & & ! base_script - > get_path ( ) . is_empty ( ) ? base_script : nullptr ;
2017-10-02 23:24:00 +02:00
}
2023-01-19 19:12:25 +01:00
StringName CSharpScript : : get_global_name ( ) const {
2023-02-02 00:54:16 +01:00
return global_class ? StringName ( class_name ) : StringName ( ) ;
2023-01-19 19:12:25 +01:00
}
2021-10-22 19:25:01 +02:00
void CSharpScript : : get_script_property_list ( List < PropertyInfo > * r_list ) const {
2022-07-31 11:07:48 +03:00
# ifdef TOOLS_ENABLED
2022-05-28 04:56:46 +02:00
const CSharpScript * top = this ;
while ( top ! = nullptr ) {
for ( const PropertyInfo & E : top - > exported_members_cache ) {
r_list - > push_back ( E ) ;
}
top = top - > base_script . ptr ( ) ;
2022-07-31 11:07:48 +03:00
}
# else
2022-05-28 04:56:46 +02:00
const CSharpScript * top = this ;
while ( top ! = nullptr ) {
List < PropertyInfo > props ;
2021-10-30 16:55:21 +02:00
2022-05-28 04:56:46 +02:00
for ( const KeyValue < StringName , PropertyInfo > & E : top - > member_info ) {
props . push_front ( E . value ) ;
}
for ( const PropertyInfo & prop : props ) {
r_list - > push_back ( prop ) ;
}
top = top - > base_script . ptr ( ) ;
2017-10-02 23:24:00 +02:00
}
2022-05-28 04:56:46 +02:00
# endif
2017-10-02 23:24:00 +02:00
}
int CSharpScript : : get_member_line ( const StringName & p_member ) const {
// TODO omnisharp
return - 1 ;
}
2022-07-12 23:12:42 +02:00
const Variant CSharpScript : : get_rpc_config ( ) const {
return rpc_config ;
2020-02-12 11:51:50 +01:00
}
2017-10-02 23:24:00 +02:00
Error CSharpScript : : load_source_code ( const String & p_path ) {
2018-10-22 19:43:19 +02:00
Error ferr = read_all_file_utf8 ( p_path , source ) ;
2019-08-09 03:39:45 +02:00
ERR_FAIL_COND_V_MSG ( ferr ! = OK , ferr ,
2021-10-28 15:57:41 +02:00
ferr = = ERR_INVALID_DATA
? " Script ' " + p_path + " ' contains invalid unicode (UTF-8), so it was not loaded. "
" Please ensure that scripts are saved in valid UTF-8 unicode. "
: " Failed to read file: ' " + p_path + " '. " ) ;
2017-10-02 23:24:00 +02:00
# ifdef TOOLS_ENABLED
source_changed_cache = true ;
# endif
return OK ;
}
2020-07-05 19:19:36 +02:00
void CSharpScript : : _clear ( ) {
tool = false ;
valid = false ;
2021-12-06 19:45:59 +01:00
reload_invalidated = true ;
2017-10-02 23:24:00 +02:00
}
2020-03-14 19:20:17 +01:00
CSharpScript : : CSharpScript ( ) {
2017-10-02 23:24:00 +02:00
_clear ( ) ;
# ifdef DEBUG_ENABLED
2018-11-30 20:43:06 +01:00
{
2020-02-26 11:28:13 +01:00
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > script_instances_mutex ) ;
2018-11-30 20:43:06 +01:00
CSharpLanguage : : get_singleton ( ) - > script_list . add ( & this - > script_list ) ;
}
2017-10-02 23:24:00 +02:00
# endif
}
CSharpScript : : ~ CSharpScript ( ) {
# ifdef DEBUG_ENABLED
2020-02-26 11:28:13 +01:00
MutexLock lock ( CSharpLanguage : : get_singleton ( ) - > script_instances_mutex ) ;
2018-11-30 20:43:06 +01:00
CSharpLanguage : : get_singleton ( ) - > script_list . remove ( & this - > script_list ) ;
2017-10-02 23:24:00 +02:00
# endif
2021-09-12 20:21:15 +02:00
2021-09-12 20:23:05 +02:00
if ( GDMonoCache : : godot_api_cache_updated ) {
GDMonoCache : : managed_callbacks . ScriptManagerBridge_RemoveScriptBridge ( this ) ;
2021-09-12 20:21:15 +02:00
}
2017-10-02 23:24:00 +02:00
}
2022-05-19 17:00:06 +02:00
void CSharpScript : : get_members ( HashSet < StringName > * p_members ) {
2020-05-22 00:58:34 +02:00
# if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
if ( p_members ) {
2021-07-26 21:31:17 +02:00
for ( const StringName & member_name : exported_members_names ) {
p_members - > insert ( member_name ) ;
2020-05-22 00:58:34 +02:00
}
}
# endif
}
2017-10-02 23:24:00 +02:00
/*************** RESOURCE ***************/
2022-05-03 01:43:50 +02:00
Ref < Resource > ResourceFormatLoaderCSharpScript : : load ( const String & p_path , const String & p_original_path , Error * r_error , bool p_use_sub_threads , float * r_progress , CacheMode p_cache_mode ) {
2020-07-05 19:19:36 +02:00
if ( r_error ) {
2017-10-02 23:24:00 +02:00
* r_error = ERR_FILE_CANT_OPEN ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
// TODO ignore anything inside bin/ and obj/ in tools builds?
2023-03-24 12:38:00 +01:00
Ref < CSharpScript > scr ;
2017-10-02 23:24:00 +02:00
2022-02-27 21:57:36 +01:00
if ( GDMonoCache : : godot_api_cache_updated ) {
2023-03-24 12:38:00 +01:00
GDMonoCache : : managed_callbacks . ScriptManagerBridge_GetOrCreateScriptBridgeForPath ( & p_path , & scr ) ;
2022-02-27 21:57:36 +01:00
} else {
2023-03-24 12:38:00 +01:00
scr = Ref < CSharpScript > ( memnew ( CSharpScript ) ) ;
2022-02-27 21:57:36 +01:00
}
2017-10-02 23:24:00 +02:00
# if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
2023-03-24 12:38:00 +01:00
Error err = scr - > load_source_code ( p_path ) ;
2022-05-03 01:43:50 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , Ref < Resource > ( ) , " Cannot load C# script file ' " + p_path + " '. " ) ;
2017-10-02 23:24:00 +02:00
# endif
2023-03-24 12:38:00 +01:00
scr - > set_path ( p_original_path ) ;
2017-10-07 00:43:02 +02:00
2023-03-24 12:38:00 +01:00
scr - > reload ( ) ;
2017-10-02 23:24:00 +02:00
2020-07-05 19:19:36 +02:00
if ( r_error ) {
2017-10-02 23:24:00 +02:00
* r_error = OK ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
2023-03-24 12:38:00 +01:00
return scr ;
2017-10-02 23:24:00 +02:00
}
void ResourceFormatLoaderCSharpScript : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " cs " ) ;
}
bool ResourceFormatLoaderCSharpScript : : handles_type ( const String & p_type ) const {
return p_type = = " Script " | | p_type = = CSharpLanguage : : get_singleton ( ) - > get_type ( ) ;
}
String ResourceFormatLoaderCSharpScript : : get_resource_type ( const String & p_path ) const {
return p_path . get_extension ( ) . to_lower ( ) = = " cs " ? CSharpLanguage : : get_singleton ( ) - > get_type ( ) : " " ;
}
2022-06-03 01:33:42 +02:00
Error ResourceFormatSaverCSharpScript : : save ( const Ref < Resource > & p_resource , const String & p_path , uint32_t p_flags ) {
2017-10-02 23:24:00 +02:00
Ref < CSharpScript > sqscr = p_resource ;
ERR_FAIL_COND_V ( sqscr . is_null ( ) , ERR_INVALID_PARAMETER ) ;
String source = sqscr - > get_source_code ( ) ;
# ifdef TOOLS_ENABLED
if ( ! FileAccess : : exists ( p_path ) ) {
2020-07-20 15:48:12 +02:00
// 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 ( ) ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " C# project could not be created; cannot add file: ' " + p_path + " '. " ) ;
2017-10-02 23:24:00 +02:00
}
}
# endif
2022-04-12 10:12:40 +03:00
{
Error err ;
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Cannot save C# script file ' " + p_path + " '. " ) ;
2017-10-02 23:24:00 +02:00
2022-04-12 10:12:40 +03:00
file - > store_string ( source ) ;
2017-10-02 23:24:00 +02:00
2022-04-12 10:12:40 +03:00
if ( file - > get_error ( ) ! = OK & & file - > get_error ( ) ! = ERR_FILE_EOF ) {
return ERR_CANT_CREATE ;
}
2017-10-02 23:24:00 +02:00
}
2019-01-22 18:33:36 +01:00
# ifdef TOOLS_ENABLED
2017-10-02 23:24:00 +02:00
if ( ScriptServer : : is_reload_scripts_on_save_enabled ( ) ) {
CSharpLanguage : : get_singleton ( ) - > reload_tool_script ( p_resource , false ) ;
}
2019-01-22 18:33:36 +01:00
# endif
2017-10-02 23:24:00 +02:00
return OK ;
}
2022-05-03 01:43:50 +02:00
void ResourceFormatSaverCSharpScript : : get_recognized_extensions ( const Ref < Resource > & p_resource , List < String > * p_extensions ) const {
2017-10-02 23:24:00 +02:00
if ( Object : : cast_to < CSharpScript > ( p_resource . ptr ( ) ) ) {
p_extensions - > push_back ( " cs " ) ;
}
}
2022-05-03 01:43:50 +02:00
bool ResourceFormatSaverCSharpScript : : recognize ( const Ref < Resource > & p_resource ) const {
2020-04-02 01:20:12 +02:00
return Object : : cast_to < CSharpScript > ( p_resource . ptr ( ) ) ! = nullptr ;
2017-10-02 23:24:00 +02:00
}