2017-10-02 23:24:00 +02:00
/*************************************************************************/
/* csharp_script.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-10-02 23:24:00 +02:00
/* */
/* 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"
# include <mono/metadata/threads.h>
2021-03-06 00:12:42 +01:00
# include <mono/metadata/tokentype.h>
2020-02-12 11:51:50 +01:00
# include <stdint.h>
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"
2017-10-02 23:24:00 +02:00
# ifdef TOOLS_ENABLED
2021-09-13 14:33:56 +01:00
# include "core/os/keyboard.h"
2017-10-02 23:24:00 +02:00
# include "editor/bindings_generator.h"
# include "editor/editor_node.h"
2021-09-13 14:33:56 +01:00
# include "editor/editor_settings.h"
2019-12-24 15:17:23 +08:00
# include "editor/node_dock.h"
2017-10-02 23:24:00 +02:00
# endif
2019-07-09 23:33:29 +02:00
# ifdef DEBUG_METHODS_ENABLED
# include "class_db_api_json.h"
# endif
2019-07-03 09:44:53 +02:00
# include "editor/editor_internal_calls.h"
2021-10-11 11:30:59 +02:00
# include "editor_templates/templates.gen.h"
2017-10-02 23:24:00 +02:00
# include "godotsharp_dirs.h"
2019-11-10 17:10:38 +01:00
# include "mono_gd/gd_mono_cache.h"
2017-10-02 23:24:00 +02:00
# include "mono_gd/gd_mono_class.h"
# include "mono_gd/gd_mono_marshal.h"
2019-11-10 17:10:38 +01:00
# include "mono_gd/gd_mono_utils.h"
2017-10-02 23:24:00 +02:00
# include "signal_awaiter_utils.h"
2018-06-26 21:03:42 +02:00
# include "utils/macros.h"
2018-10-22 19:43:19 +02:00
# include "utils/string_utils.h"
2017-10-02 23:24:00 +02:00
2017-10-16 03:54:23 +02:00
# define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
2017-10-02 23:24:00 +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 ( ) {
String sln_path = GodotSharpDirs : : get_project_sln_path ( ) ;
String csproj_path = GodotSharpDirs : : get_project_csproj_path ( ) ;
if ( ! FileAccess : : exists ( sln_path ) | | ! FileAccess : : exists ( csproj_path ) ) {
// A solution does not yet exist, create a new one
2020-03-14 19:20:17 +01:00
CRASH_COND ( CSharpLanguage : : get_singleton ( ) - > get_godotsharp_editor ( ) = = nullptr ) ;
2019-07-03 09:44:53 +02:00
return CSharpLanguage : : get_singleton ( ) - > get_godotsharp_editor ( ) - > call ( " CreateProjectSolution " ) ;
2017-10-05 00:10:51 +02:00
}
return true ;
}
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
2021-08-16 17:16:36 +02:00
GDNativeInstanceBindingCallbacks CSharpLanguage : : _instance_binding_callbacks = {
& _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 " ;
}
Error CSharpLanguage : : execute_file ( const String & p_path ) {
// ??
return OK ;
}
void CSharpLanguage : : init ( ) {
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
2017-10-02 23:24:00 +02:00
gdmono = memnew ( GDMono ) ;
gdmono - > initialize ( ) ;
# if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
2019-08-09 03:39:45 +02:00
// Generate bindings here, before loading assemblies. 'initialize_load_assemblies' aborts
2019-07-11 14:01:25 +02:00
// the applications if the api assemblies or the main tools assembly is missing, but this
// is not a problem for BindingsGenerator as it only needs the tools project editor assembly.
2019-07-03 09:44:53 +02:00
List < String > cmdline_args = OS : : get_singleton ( ) - > get_cmdline_args ( ) ;
BindingsGenerator : : handle_cmdline_args ( cmdline_args ) ;
2017-10-02 23:24:00 +02:00
# endif
2019-07-11 14:01:25 +02:00
# ifndef MONO_GLUE_ENABLED
2019-08-09 03:39:45 +02:00
print_line ( " Run this binary with '--generate-mono-glue path/to/modules/mono/glue' " ) ;
2019-07-11 14:01:25 +02:00
# endif
2020-07-05 19:19:36 +02:00
if ( gdmono - > is_runtime_initialized ( ) ) {
2019-11-29 01:35:46 +01:00
gdmono - > initialize_load_assemblies ( ) ;
2020-07-05 19:19:36 +02:00
}
2019-07-11 14:01:25 +02:00
2017-10-02 23:24:00 +02: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
}
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
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
}
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 ) {
String base_class = p_base_class_name ;
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 {
2017-10-02 23:24:00 +02:00
Ref < CSharpScript > script ;
2021-06-17 16:03:09 -06:00
script . 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 ( ) ) ;
script - > set_source_code ( processed_template ) ;
2017-10-02 23:24:00 +02:00
return script ;
}
2021-10-11 11:30:59 +02:00
Vector < ScriptLanguage : : ScriptTemplate > CSharpLanguage : : get_built_in_templates ( StringName p_object ) {
Vector < ScriptLanguage : : ScriptTemplate > templates ;
for ( int i = 0 ; i < TEMPLATES_ARRAY_SIZE ; i + + ) {
if ( TEMPLATES [ i ] . inherit = = p_object ) {
templates . append ( TEMPLATES [ i ] ) ;
}
}
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 ) ) {
return TTR ( " Class name can't be a reserved keyword " ) ;
}
return " " ;
}
2017-10-02 23:24:00 +02:00
Script * CSharpLanguage : : create_script ( ) const {
return memnew ( CSharpScript ) ;
}
bool CSharpLanguage : : has_named_classes ( ) const {
2017-10-24 01:54:47 +02:00
return false ;
}
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
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 ( ) ) {
2017-10-16 04:17:06 +02:00
return " object " ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
if ( ! ClassDB : : class_exists ( p_var_type_name ) ) {
2018-11-30 20:43:06 +01:00
return p_var_type_name ;
}
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( Variant : : OBJECT ) ) {
2018-11-30 20:43:06 +01:00
return " Godot.Object " ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
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
# ifdef REAL_T_IS_DOUBLE
return " double " ;
# else
return " float " ;
# endif
}
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
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 ) ) {
2020-03-14 19:20:17 +01:00
return " SignalInfo " ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
2018-11-30 20:43:06 +01:00
Variant : : Type var_types [ ] = {
Variant : : BOOL ,
Variant : : INT ,
Variant : : VECTOR2 ,
2020-03-03 04:42:20 -05:00
Variant : : VECTOR2I ,
2018-11-30 20:43:06 +01:00
Variant : : RECT2 ,
2020-03-03 04:42:20 -05:00
Variant : : RECT2I ,
2018-11-30 20:43:06 +01:00
Variant : : VECTOR3 ,
2020-03-03 04:42:20 -05:00
Variant : : VECTOR3I ,
2018-11-30 20:43:06 +01:00
Variant : : TRANSFORM2D ,
Variant : : PLANE ,
2021-01-20 07:02:02 +00:00
Variant : : QUATERNION ,
2018-11-30 20:43:06 +01:00
Variant : : AABB ,
Variant : : BASIS ,
2021-04-28 03:36:08 -04:00
Variant : : TRANSFORM3D ,
2018-11-30 20:43:06 +01:00
Variant : : COLOR ,
2020-03-14 19:20:17 +01:00
Variant : : STRING_NAME ,
2018-11-30 20:43:06 +01:00
Variant : : NODE_PATH ,
2020-11-09 14:53:05 +01:00
Variant : : RID ,
2020-03-14 19:20:17 +01:00
Variant : : CALLABLE
2018-11-30 20:43:06 +01:00
} ;
2017-10-16 04:17:06 +02:00
2019-02-21 20:57:39 +01:00
for ( unsigned int i = 0 ; i < sizeof ( var_types ) / sizeof ( Variant : : Type ) ; i + + ) {
2020-07-05 19:19:36 +02:00
if ( p_var_type_name = = Variant : : get_type_name ( var_types [ i ] ) ) {
2018-11-30 20:43:06 +01:00
return p_var_type_name ;
2020-07-05 19:19:36 +02:00
}
2017-10-16 04:17:06 +02:00
}
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
return " object " ;
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 ( ) ) {
2021-08-15 18:14:46 +01:00
bool use_space_indentation = EDITOR_DEF ( " text_editor/behavior/indent/type " , 0 ) ;
2017-10-30 22:17:20 +01:00
if ( use_space_indentation ) {
2021-08-15 18:14:46 +01:00
int indent_size = EDITOR_DEF ( " text_editor/behavior/indent/size " , 4 ) ;
2017-10-30 22:17:20 +01:00
String space_indent = " " ;
for ( int i = 0 ; i < indent_size ; i + + ) {
space_indent + = " " ;
}
return space_indent ;
}
}
# endif
return " \t " ;
}
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 ;
SCOPE_EXIT { _recursion_flag_ = false ; } ;
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-01-09 17:19:03 +01:00
2020-07-05 19:19:36 +02:00
if ( ! gdmono - > is_runtime_initialized ( ) | | ! GDMono : : get_singleton ( ) - > get_core_api_assembly ( ) | | ! GDMonoCache : : cached_data . corlib_cache_updated ) {
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
MonoObject * stack_trace = mono_object_new ( mono_domain_get ( ) , CACHED_CLASS ( System_Diagnostics_StackTrace ) - > get_mono_ptr ( ) ) ;
MonoBoolean need_file_info = true ;
void * ctor_args [ 1 ] = { & need_file_info } ;
CACHED_METHOD ( System_Diagnostics_StackTrace , ctor_bool ) - > invoke_raw ( stack_trace , ctor_args ) ;
Vector < StackInfo > si ;
si = stack_trace_get_info ( stack_trace ) ;
return si ;
2018-01-27 18:44:04 +01:00
# else
return Vector < StackInfo > ( ) ;
# endif
2018-01-09 17:19:03 +01:00
}
2018-01-27 18:44:04 +01:00
# ifdef DEBUG_ENABLED
2018-01-09 17:19:03 +01:00
Vector < ScriptLanguage : : StackInfo > CSharpLanguage : : stack_trace_get_info ( MonoObject * p_stack_trace ) {
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 ;
SCOPE_EXIT { _recursion_flag_ = false ; } ;
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-01-09 17:19:03 +01:00
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2018-01-09 17:19:03 +01:00
2019-11-10 17:10:38 +01:00
MonoArray * frames = CACHED_METHOD_THUNK ( System_Diagnostics_StackTrace , GetFrames ) . invoke ( p_stack_trace , & exc ) ;
2018-01-09 17:19:03 +01:00
if ( exc ) {
2018-06-26 21:03:42 +02:00
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
2018-01-09 17:19:03 +01:00
return Vector < StackInfo > ( ) ;
}
int frame_count = mono_array_length ( frames ) ;
2020-07-05 19:19:36 +02:00
if ( frame_count < = 0 ) {
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 ;
si . resize ( frame_count ) ;
for ( int i = 0 ; i < frame_count ; i + + ) {
2018-07-25 03:11:03 +02:00
StackInfo & sif = si . write [ i ] ;
2018-01-09 17:19:03 +01:00
MonoObject * frame = mono_array_get ( frames , MonoObject * , i ) ;
MonoString * file_name ;
int file_line_num ;
MonoString * method_decl ;
2019-11-10 17:10:38 +01:00
CACHED_METHOD_THUNK ( DebuggingUtils , GetStackFrameInfo ) . invoke ( frame , & file_name , & file_line_num , & method_decl , & exc ) ;
2018-01-09 17:19:03 +01:00
if ( exc ) {
2018-06-26 21:03:42 +02:00
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
2018-01-09 17:19:03 +01:00
return Vector < StackInfo > ( ) ;
}
2018-01-12 19:23:11 +01:00
// TODO
// what if the StackFrame method is null (method_decl is empty). should we skip this frame?
// can reproduce with a MissingMethodException on internal calls
2018-01-09 17:19:03 +01:00
sif . file = GDMonoMarshal : : mono_string_to_godot ( file_name ) ;
sif . line = file_line_num ;
sif . func = GDMonoMarshal : : mono_string_to_godot ( method_decl ) ;
}
return si ;
}
2018-01-27 18:44:04 +01:00
# endif
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 ( ) ;
2020-01-23 21:23:32 +01:00
Map < ObjectID , int > : : Element * elem = unsafe_object_references . find ( id ) ;
2020-01-13 21:00:07 +01:00
ERR_FAIL_NULL ( elem ) ;
2020-07-05 19:19:36 +02:00
if ( - - elem - > value ( ) = = 0 ) {
2020-01-23 21:23:32 +01:00
unsafe_object_references . erase ( 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 ( ) {
2020-04-02 01:20:12 +02:00
if ( gdmono & & gdmono - > is_runtime_initialized ( ) & & gdmono - > get_core_api_assembly ( ) ! = nullptr ) {
2019-12-11 17:08:40 +01:00
const Ref < MonoGCHandleRef > & task_scheduler_handle = GDMonoCache : : cached_data . task_scheduler_handle ;
2017-10-02 23:24:00 +02:00
2018-09-12 02:41:54 +02:00
if ( task_scheduler_handle . is_valid ( ) ) {
MonoObject * task_scheduler = task_scheduler_handle - > get_target ( ) ;
2017-10-02 23:24:00 +02:00
2018-09-12 02:41:54 +02:00
if ( task_scheduler ) {
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2019-11-10 17:10:38 +01:00
CACHED_METHOD_THUNK ( GodotTaskScheduler , Activate ) . invoke ( task_scheduler , & exc ) ;
2017-10-02 23:24:00 +02:00
2018-09-12 02:41:54 +02:00
if ( exc ) {
GDMonoUtils : : debug_unhandled_exception ( exc ) ;
}
2017-10-02 23:24:00 +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 {
2020-07-05 19:19:36 +02:00
if ( A = = B ) {
2017-10-02 23:24:00 +02:00
return false ; // shouldn't happen but..
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
GDMonoClass * I = B - > base ;
while ( I ) {
if ( I = = A - > script_class ) {
// A is a base of B
return true ;
}
I = I - > get_parent_class ( ) ;
}
return false ; // not a base
}
} ;
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 ( ) ) {
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2019-01-21 22:44:05 +01:00
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 ( ) ) {
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-11-30 20:43:06 +01:00
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 ( ) {
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
2018-07-25 21:54:21 +02:00
GDMonoAssembly * proj_assembly = gdmono - > get_project_assembly ( ) ;
2017-10-02 23:24:00 +02:00
2021-11-25 01:45:33 -06:00
String appname_safe = ProjectSettings : : get_singleton ( ) - > get_safe_project_name ( ) ;
2018-07-25 21:31:02 +02:00
2019-07-03 17:41:07 +02:00
appname_safe + = " .dll " ;
2017-10-02 23:24:00 +02:00
2018-07-25 21:54:21 +02:00
if ( proj_assembly ) {
String proj_asm_path = proj_assembly - > get_path ( ) ;
2017-10-02 23:24:00 +02:00
2020-03-18 17:40:04 +01:00
if ( ! FileAccess : : exists ( proj_asm_path ) ) {
2018-07-25 21:54:21 +02:00
// Maybe it wasn't loaded from the default path, so check this as well
2019-07-03 17:41:07 +02:00
proj_asm_path = GodotSharpDirs : : get_res_temp_assemblies_dir ( ) . plus_file ( appname_safe ) ;
2020-07-05 19:19:36 +02:00
if ( ! FileAccess : : exists ( proj_asm_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-07-25 21:54:21 +02:00
2020-07-05 19:19:36 +02:00
if ( FileAccess : : get_modified_time ( proj_asm_path ) < = proj_assembly - > get_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 {
2020-07-05 19:19:36 +02:00
if ( ! FileAccess : : exists ( GodotSharpDirs : : get_res_temp_assemblies_dir ( ) . plus_file ( appname_safe ) ) ) {
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 ) {
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
// 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 ( ) ) {
2019-07-03 09:44:53 +02:00
// 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 ( ) ;
2019-12-11 17:08:40 +01:00
MonoDelegate * delegate = ( MonoDelegate * ) managed_callable - > delegate_handle . get_target ( ) ;
2020-03-14 19:20:17 +01:00
Array serialized_data ;
MonoObject * managed_serialized_data = GDMonoMarshal : : variant_to_mono_object ( serialized_data ) ;
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2020-03-14 19:20:17 +01:00
bool success = ( bool ) CACHED_METHOD_THUNK ( DelegateUtils , TrySerializeDelegate ) . invoke ( delegate , managed_serialized_data , & exc ) ;
if ( exc ) {
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
continue ;
}
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
2021-07-26 17:50:35 +02:00
for ( Ref < CSharpScript > & script : 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.
if ( script - > get_path ( ) . is_empty ( ) & & ! script - > script_class ) {
continue ;
}
2018-11-30 20:43:06 +01:00
to_reload . push_back ( script ) ;
2017-10-02 23:24:00 +02:00
2020-12-15 12:04:21 +00:00
if ( script - > get_path ( ) . is_empty ( ) ) {
2020-05-09 17:11:35 +02:00
script - > tied_class_name_for_reload = script - > script_class - > get_name_for_lookup ( ) ;
2019-07-03 09:44:53 +02:00
script - > tied_class_namespace_for_reload = script - > script_class - > get_namespace ( ) ;
}
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
2021-07-26 21:31:17 +02:00
for ( Object * & obj : script - > instances ) {
2019-07-03 09:44:53 +02:00
script - > pending_reload_instances . insert ( obj - > get_instance_id ( ) ) ;
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
2021-07-26 21:31:17 +02:00
for ( PlaceHolderScriptInstance * & script_instance : script - > placeholders ) {
Object * obj = script_instance - > get_owner ( ) ;
2019-07-03 09:44:53 +02:00
script - > pending_reload_instances . insert ( obj - > get_instance_id ( ) ) ;
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
Map < ObjectID , CSharpScript : : StateBackup > & owners_map = script - > pending_reload_state ;
2017-10-02 23:24:00 +02:00
2021-07-26 21:31:17 +02:00
for ( Object * & obj : script - > 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
2019-07-03 09:44:53 +02:00
// Call OnBeforeSerialize
2020-07-05 19:19:36 +02:00
if ( csi - > script - > script_class - > implements_interface ( CACHED_CLASS ( ISerializationListener ) ) ) {
2020-07-24 14:13:58 -03:00
obj - > get_script_instance ( ) - > call ( string_names . on_before_serialize ) ;
2020-07-05 19:19:36 +02:00
}
2019-07-03 09:44:53 +02:00
// Save instance info
CSharpScript : : StateBackup state ;
// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
csi - > get_properties_state_for_reloading ( state . properties ) ;
2020-03-14 19:20:17 +01:00
csi - > get_event_signals_state_for_reloading ( state . event_signals ) ;
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
2021-07-26 17:50:35 +02:00
for ( Ref < CSharpScript > & script : scripts ) {
2019-07-03 09:44:53 +02:00
while ( script - > instances . front ( ) ) {
Object * obj = script - > instances . front ( ) - > get ( ) ;
2020-02-13 16:03:10 -03:00
obj - > set_script ( REF ( ) ) ; // 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
script - > _clear ( ) ;
2017-10-02 23:24:00 +02:00
}
2018-11-30 20:43:06 +01:00
// Do domain reload
2018-07-29 22:40:09 +02:00
if ( gdmono - > reload_scripts_domain ( ) ! = OK ) {
// 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
}
}
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
2021-07-26 17:50:35 +02:00
for ( Ref < CSharpScript > & script : to_reload ) {
2019-01-22 18:33:36 +01:00
# ifdef TOOLS_ENABLED
2020-10-26 06:59:08 +01:00
script - > exports_invalidated = true ;
2019-01-22 18:33:36 +01:00
# endif
2020-10-26 06:59:08 +01:00
script - > signals_invalidated = true ;
2019-07-03 09:44:53 +02:00
2020-12-15 12:04:21 +00:00
if ( ! script - > get_path ( ) . is_empty ( ) ) {
2019-07-03 09:44:53 +02:00
script - > reload ( p_soft_reload ) ;
2019-07-08 15:18:38 +02:00
if ( ! script - > valid ) {
script - > pending_reload_instances . clear ( ) ;
continue ;
}
2019-07-03 09:44:53 +02:00
} else {
const StringName & class_namespace = script - > tied_class_namespace_for_reload ;
const StringName & class_name = script - > tied_class_name_for_reload ;
GDMonoAssembly * project_assembly = gdmono - > get_project_assembly ( ) ;
// Search in project and tools assemblies first as those are the most likely to have the class
2020-04-02 01:20:12 +02:00
GDMonoClass * script_class = ( project_assembly ? project_assembly - > get_class ( class_namespace , class_name ) : nullptr ) ;
2019-07-08 15:22:51 +02:00
# ifdef TOOLS_ENABLED
2019-07-03 09:44:53 +02:00
if ( ! script_class ) {
2019-07-08 15:22:51 +02:00
GDMonoAssembly * tools_assembly = gdmono - > get_tools_assembly ( ) ;
2020-04-02 01:20:12 +02:00
script_class = ( tools_assembly ? tools_assembly - > get_class ( class_namespace , class_name ) : nullptr ) ;
2019-07-03 09:44:53 +02:00
}
2019-07-08 15:22:51 +02:00
# endif
2019-07-03 09:44:53 +02:00
if ( ! script_class ) {
script_class = gdmono - > get_class ( class_namespace , class_name ) ;
}
if ( ! script_class ) {
// The class was removed, can't reload
script - > pending_reload_instances . clear ( ) ;
continue ;
}
bool obj_type = CACHED_CLASS ( GodotObject ) - > is_assignable_from ( script_class ) ;
if ( ! obj_type ) {
// The class no longer inherits Godot.Object, can't reload
script - > pending_reload_instances . clear ( ) ;
continue ;
}
GDMonoClass * native = GDMonoUtils : : get_class_native_base ( script_class ) ;
2019-07-08 15:18:38 +02:00
CSharpScript : : initialize_for_managed_type ( script , script_class , native ) ;
2019-07-03 09:44:53 +02:00
}
2020-03-14 19:20:17 +01:00
StringName native_name = NATIVE_GDMONOCLASS_NAME ( script - > native ) ;
2017-10-02 23:24:00 +02:00
2018-11-30 20:43:06 +01:00
{
2021-07-26 21:31:17 +02:00
for ( const ObjectID & obj_id : script - > pending_reload_instances ) {
2018-11-30 20:43:06 +01:00
Object * obj = ObjectDB : : get_instance ( obj_id ) ;
if ( ! obj ) {
2019-07-03 09:44:53 +02:00
script - > pending_reload_state . erase ( obj_id ) ;
continue ;
}
if ( ! ClassDB : : is_parent_class ( obj - > get_class_name ( ) , native_name ) ) {
// No longer inherits the same compatible type, can't reload
script - > 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
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 ( ) ) ;
2019-07-03 09:44:53 +02:00
if ( script - > is_tool ( ) | | ScriptServer : : is_scripting_enabled ( ) ) {
2018-11-30 20:43:06 +01:00
// Replace placeholder with a script instance
2019-07-03 09:44:53 +02:00
CSharpScript : : StateBackup & state_backup = script - > pending_reload_state [ obj_id ] ;
2018-11-30 20:43:06 +01: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
2019-07-03 09:44:53 +02:00
ScriptInstance * script_instance = script - > instance_create ( obj ) ;
2018-11-30 20:43:06 +01:00
if ( script_instance ) {
2019-07-03 09:44:53 +02:00
script - > placeholders . erase ( static_cast < PlaceHolderScriptInstance * > ( si ) ) ;
2018-11-30 20:43:06 +01:00
obj - > set_script_instance ( script_instance ) ;
}
}
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
// Re-create script instance
2020-02-13 16:03:10 -03:00
obj - > set_script ( script ) ; // will create the script instance as well
2019-07-03 09:44:53 +02:00
}
}
2018-11-30 20:43:06 +01:00
2019-07-03 09:44:53 +02:00
to_reload_state . push_back ( script ) ;
}
2018-11-30 20:43:06 +01:00
2021-07-26 17:50:35 +02:00
for ( Ref < CSharpScript > & script : to_reload_state ) {
2021-07-26 21:31:17 +02:00
for ( const ObjectID & obj_id : script - > pending_reload_instances ) {
2019-07-03 09:44:53 +02:00
Object * obj = ObjectDB : : get_instance ( obj_id ) ;
if ( ! obj ) {
script - > pending_reload_state . erase ( obj_id ) ;
continue ;
2017-10-02 23:24:00 +02:00
}
2019-07-03 09:44:53 +02:00
ERR_CONTINUE ( ! obj - > get_script_instance ( ) ) ;
// TODO: Restore serialized state
CSharpScript : : StateBackup & state_backup = script - > pending_reload_state [ obj_id ] ;
2021-07-26 21:31:17 +02:00
for ( const Pair < StringName , Variant > & G : state_backup . properties ) {
obj - > get_script_instance ( ) - > set ( G . first , G . second ) ;
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 ) {
2021-07-26 21:31:17 +02:00
for ( const Pair < StringName , Array > & G : state_backup . event_signals ) {
const StringName & name = G . first ;
const Array & serialized_data = G . second ;
2020-03-14 19:20:17 +01:00
Map < StringName , CSharpScript : : EventSignal > : : Element * match = script - > event_signals . find ( name ) ;
if ( ! match ) {
// The event or its signal attribute were removed
continue ;
}
const CSharpScript : : EventSignal & event_signal = match - > value ( ) ;
MonoObject * managed_serialized_data = GDMonoMarshal : : variant_to_mono_object ( serialized_data ) ;
2020-04-02 01:20:12 +02:00
MonoDelegate * delegate = nullptr ;
2020-03-14 19:20:17 +01:00
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2020-03-14 19:20:17 +01:00
bool success = ( bool ) CACHED_METHOD_THUNK ( DelegateUtils , TryDeserializeDelegate ) . invoke ( managed_serialized_data , & delegate , & exc ) ;
if ( exc ) {
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
continue ;
}
if ( success ) {
2020-04-02 01:20:12 +02:00
ERR_CONTINUE ( delegate = = nullptr ) ;
2020-03-14 19:20:17 +01:00
event_signal . field - > set_value ( csi - > get_mono_object ( ) , ( MonoObject * ) delegate ) ;
} else if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
OS : : get_singleton ( ) - > print ( " Failed to deserialize event signal delegate \n " ) ;
}
}
// Call OnAfterDeserialization
2020-07-05 19:19:36 +02:00
if ( csi - > script - > script_class - > implements_interface ( CACHED_CLASS ( ISerializationListener ) ) ) {
2020-07-24 14:13:58 -03:00
obj - > get_script_instance ( ) - > call ( string_names . on_after_deserialize ) ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
}
2017-10-02 23:24:00 +02:00
}
2019-07-03 09:44:53 +02:00
script - > pending_reload_instances . 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
MonoObject * managed_serialized_data = GDMonoMarshal : : variant_to_mono_object ( serialized_data ) ;
2020-04-02 01:20:12 +02:00
MonoDelegate * delegate = nullptr ;
2020-03-14 19:20:17 +01:00
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2020-03-14 19:20:17 +01:00
bool success = ( bool ) CACHED_METHOD_THUNK ( DelegateUtils , TryDeserializeDelegate ) . invoke ( managed_serialized_data , & delegate , & exc ) ;
if ( exc ) {
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
continue ;
}
if ( success ) {
2020-04-02 01:20:12 +02:00
ERR_CONTINUE ( delegate = = nullptr ) ;
2020-03-14 19:20:17 +01:00
managed_callable - > set_delegate ( delegate ) ;
} 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
2017-10-02 23:24:00 +02:00
}
# endif
2021-03-06 00:12:42 +01:00
void CSharpLanguage : : lookup_script_for_class ( GDMonoClass * p_class ) {
if ( ! p_class - > has_attribute ( CACHED_CLASS ( ScriptPathAttribute ) ) ) {
return ;
}
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
MonoObject * attr = p_class - > get_attribute ( CACHED_CLASS ( ScriptPathAttribute ) ) ;
String path = CACHED_FIELD ( ScriptPathAttribute , path ) - > get_string_value ( attr ) ;
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
dotnet_script_lookup_map [ path ] = DotNetScriptLookupInfo (
p_class - > get_namespace ( ) , p_class - > get_name ( ) , p_class ) ;
}
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
void CSharpLanguage : : lookup_scripts_in_assembly ( GDMonoAssembly * p_assembly ) {
if ( p_assembly - > has_attribute ( CACHED_CLASS ( AssemblyHasScriptsAttribute ) ) ) {
MonoObject * attr = p_assembly - > get_attribute ( CACHED_CLASS ( AssemblyHasScriptsAttribute ) ) ;
bool requires_lookup = CACHED_FIELD ( AssemblyHasScriptsAttribute , requiresLookup ) - > get_bool_value ( attr ) ;
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
if ( requires_lookup ) {
// This is supported for scenarios where specifying all types would be cumbersome,
// such as when disabling C# source generators (for whatever reason) or when using a
// language other than C# that has nothing similar to source generators to automate it.
MonoImage * image = p_assembly - > get_image ( ) ;
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
int rows = mono_image_get_table_rows ( image , MONO_TABLE_TYPEDEF ) ;
2019-08-09 03:39:45 +02:00
2021-03-06 00:12:42 +01:00
for ( int i = 1 ; i < rows ; i + + ) {
// We don't search inner classes, only top-level.
MonoClass * mono_class = mono_class_get ( image , ( i + 1 ) | MONO_TOKEN_TYPE_DEF ) ;
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
if ( ! mono_class_is_assignable_from ( CACHED_CLASS_RAW ( GodotObject ) , mono_class ) ) {
continue ;
}
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
GDMonoClass * current = p_assembly - > get_class ( mono_class ) ;
if ( current ) {
lookup_script_for_class ( current ) ;
}
}
} else {
// This is the most likely scenario as we use C# source generators
MonoArray * script_types = ( MonoArray * ) CACHED_FIELD ( AssemblyHasScriptsAttribute , scriptTypes ) - > get_value ( attr ) ;
2018-10-22 19:43:19 +02:00
2021-03-06 00:12:42 +01:00
int length = mono_array_length ( script_types ) ;
for ( int i = 0 ; i < length ; i + + ) {
MonoReflectionType * reftype = mono_array_get ( script_types , MonoReflectionType * , i ) ;
ManagedType type = ManagedType : : from_reftype ( reftype ) ;
ERR_CONTINUE ( ! type . type_class ) ;
lookup_script_for_class ( type . type_class ) ;
}
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
void CSharpLanguage : : thread_enter ( ) {
#if 0
2019-11-10 17:10:38 +01:00
if (gdmono->is_runtime_initialized()) {
2017-10-02 23:24:00 +02:00
GDMonoUtils::attach_current_thread();
}
#endif
}
void CSharpLanguage : : thread_exit ( ) {
#if 0
2019-11-10 17:10:38 +01:00
if (gdmono->is_runtime_initialized()) {
2017-10-02 23:24:00 +02:00
GDMonoUtils::detach_current_thread();
}
#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-04-26 01:19:54 +02:00
void CSharpLanguage : : _on_scripts_domain_unloaded ( ) {
2021-07-26 21:31:17 +02:00
for ( KeyValue < Object * , CSharpScriptBinding > & E : script_bindings ) {
CSharpScriptBinding & script_binding = E . value ;
2019-12-11 17:08:40 +01:00
script_binding . gchandle . release ( ) ;
2019-03-07 19:55:40 +01:00
script_binding . inited = false ;
}
2019-04-26 01:19:54 +02:00
2020-07-05 19:19:36 +02:00
# ifdef GD_MONO_HOT_RELOAD
2020-03-14 19:20:17 +01:00
{
MutexLock lock ( ManagedCallable : : instances_mutex ) ;
for ( SelfList < ManagedCallable > * elem = ManagedCallable : : instances . first ( ) ; elem ; elem = elem - > next ( ) ) {
ManagedCallable * managed_callable = elem - > self ( ) ;
2019-12-11 17:08:40 +01:00
managed_callable - > delegate_handle . release ( ) ;
2020-04-02 01:20:12 +02:00
managed_callable - > delegate_invoke = nullptr ;
2020-03-14 19:20:17 +01:00
}
}
2020-07-05 19:19:36 +02:00
# endif
2020-03-14 19:20:17 +01:00
2021-03-06 00:12:42 +01:00
dotnet_script_lookup_map . clear ( ) ;
2019-03-07 19:55:40 +01:00
}
2019-07-03 09:44:53 +02:00
# ifdef TOOLS_ENABLED
void CSharpLanguage : : _editor_init_callback ( ) {
register_editor_internal_calls ( ) ;
// Initialize GodotSharpEditor
GDMonoClass * editor_klass = GDMono : : get_singleton ( ) - > get_tools_assembly ( ) - > get_class ( " GodotTools " , " GodotSharpEditor " ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( editor_klass = = nullptr ) ;
2019-07-03 09:44:53 +02:00
MonoObject * mono_object = mono_object_new ( mono_domain_get ( ) , editor_klass - > get_mono_ptr ( ) ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( mono_object = = nullptr ) ;
2019-07-03 09:44:53 +02:00
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2019-07-03 09:44:53 +02:00
GDMonoUtils : : runtime_object_init ( mono_object , editor_klass , & exc ) ;
UNHANDLED_EXCEPTION ( exc ) ;
2020-07-05 19:19:36 +02:00
EditorPlugin * godotsharp_editor = Object : : cast_to < EditorPlugin > (
GDMonoMarshal : : mono_object_to_variant ( mono_object ) . operator Object * ( ) ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( godotsharp_editor = = nullptr ) ;
2019-07-03 09:44:53 +02:00
// Enable it as a plugin
EditorNode : : add_editor_plugin ( godotsharp_editor ) ;
2021-08-13 16:31:57 -05:00
ED_SHORTCUT ( " mono/build_solution " , TTR ( " Build Solution " ) , KeyModifierMask : : ALT | Key : : B ) ;
2019-07-03 09:44:53 +02:00
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
}
}
2019-12-11 17:08:40 +01:00
void CSharpLanguage : : release_script_gchandle ( MonoObject * p_expected_obj , MonoGCHandleData & p_gchandle ) {
uint32_t pinned_gchandle = GDMonoUtils : : new_strong_gchandle_pinned ( p_expected_obj ) ; // We might lock after this, so pin it
2018-09-12 02:41:54 +02:00
2019-12-11 17:08:40 +01:00
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 ) ;
2018-09-12 02:41:54 +02:00
2019-12-11 17:08:40 +01:00
MonoObject * target = p_gchandle . get_target ( ) ;
2018-09-12 02:41:54 +02:00
// We release the gchandle if it points to the MonoObject* we expect (otherwise it was
// already released and could have been replaced) or if we can't get its target MonoObject*
// (which doesn't necessarily mean it was released, and we want it released in order to
// avoid locking other threads unnecessarily).
2020-04-02 01:20:12 +02:00
if ( target = = p_expected_obj | | target = = nullptr ) {
2019-12-11 17:08:40 +01:00
p_gchandle . release ( ) ;
2018-09-12 02:41:54 +02:00
}
}
2019-12-11 17:08:40 +01:00
GDMonoUtils : : free_gchandle ( pinned_gchandle ) ;
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 ) ;
2020-07-05 19:19:36 +02:00
while ( classinfo & & ! classinfo - > exposed ) {
2017-10-09 23:50:06 +02:00
classinfo = classinfo - > inherits_ptr ;
2020-07-05 19:19:36 +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 ;
2017-10-02 23:24:00 +02:00
GDMonoClass * type_class = GDMonoUtils : : type_get_proxy_class ( type_name ) ;
2019-02-03 06:35:22 +01:00
ERR_FAIL_NULL_V ( type_class , false ) ;
2017-10-02 23:24:00 +02:00
MonoObject * mono_object = GDMonoUtils : : create_managed_for_godot_object ( type_class , type_name , p_object ) ;
2019-02-03 06:35:22 +01:00
ERR_FAIL_NULL_V ( mono_object , 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 ;
r_script_binding . wrapper_class = type_class ; // cache
2019-12-11 17:08:40 +01:00
r_script_binding . gchandle = MonoGCHandleData : : new_strong_handle ( mono_object ) ;
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 ;
}
2021-08-16 17:16:36 +02:00
Map < Object * , CSharpScriptBinding > : : Element * CSharpLanguage : : insert_script_binding ( Object * p_object , const CSharpScriptBinding & p_script_binding ) {
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 ) ;
Map < 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-08-16 17:16:36 +02:00
CRASH_COND ( ! 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
2020-01-16 17:11:13 +01:00
GD_MONO_ASSERT_THREAD_ATTACHED ;
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
2021-08-16 17:16:36 +02:00
Map < Object * , CSharpScriptBinding > : : Element * data = ( Map < 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).
2019-12-11 17:08:40 +01:00
MonoObject * mono_object = script_binding . gchandle . get_target ( ) ;
2019-02-03 06:35:22 +01:00
if ( mono_object ) {
2020-04-02 01:20:12 +02:00
CACHED_FIELD ( GodotObject , ptr ) - > set_value_raw ( mono_object , nullptr ) ;
2019-02-03 06:35:22 +01:00
}
2019-12-11 17:08:40 +01:00
script_binding . gchandle . release ( ) ;
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
}
2021-08-16 17:16:36 +02:00
GDNativeBool CSharpLanguage : : _instance_binding_reference_callback ( void * p_token , void * p_binding , GDNativeBool p_reference ) {
CRASH_COND ( ! p_binding ) ;
CSharpScriptBinding & script_binding = ( ( Map < Object * , CSharpScriptBinding > : : Element * ) p_binding ) - > get ( ) ;
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
2021-08-16 17:16:36 +02:00
int refcount = rc_owner - > reference_get_count ( ) ;
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
GD_MONO_SCOPE_THREAD_ATTACH ;
2020-01-16 17:11:13 +01:00
2021-08-16 17:16:36 +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.
MonoObject * target = gchandle . get_target ( ) ;
if ( ! target ) {
return false ; // Called after the managed side was collected, so nothing to do here
}
2018-02-22 15:34:08 +01:00
2021-08-16 17:16:36 +02:00
// Release the current weak handle and replace it with a strong handle.
MonoGCHandleData strong_gchandle = MonoGCHandleData : : new_strong_handle ( target ) ;
gchandle . release ( ) ;
gchandle = strong_gchandle ;
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
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-02-22 15:34:08 +01:00
2021-08-16 17:16:36 +02:00
// 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-08-16 17:16:36 +02:00
MonoObject * target = gchandle . get_target ( ) ;
if ( ! target ) {
return refcount = = 0 ; // Called after the managed side was collected, so nothing to do here
}
2018-02-22 15:34:08 +01:00
2021-08-16 17:16:36 +02:00
// Release the current strong handle and replace it with a weak handle.
MonoGCHandleData weak_gchandle = MonoGCHandleData : : new_weak_handle ( target ) ;
gchandle . release ( ) ;
gchandle = weak_gchandle ;
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 ) {
CSharpScriptBinding & script_binding = ( ( Map < 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
}
2019-12-11 17:08:40 +01:00
CSharpInstance * CSharpInstance : : create_for_managed_type ( Object * p_owner , CSharpScript * p_script , const MonoGCHandleData & p_gchandle ) {
2020-03-14 19:20:17 +01:00
CSharpInstance * instance = memnew ( CSharpInstance ( Ref < CSharpScript > ( p_script ) ) ) ;
2017-10-02 23:24:00 +02:00
2021-06-04 18:03:15 +02:00
RefCounted * rc = Object : : cast_to < RefCounted > ( p_owner ) ;
2017-10-02 23:24:00 +02:00
2021-06-04 18:03:15 +02:00
instance - > base_ref_counted = rc ! = nullptr ;
2017-10-02 23:24:00 +02:00
instance - > owner = p_owner ;
instance - > gchandle = p_gchandle ;
2021-06-04 18:03:15 +02:00
if ( instance - > base_ref_counted ) {
2017-10-02 23:24:00 +02:00
instance - > _reference_owner_unsafe ( ) ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
p_script - > instances . insert ( p_owner ) ;
return instance ;
}
MonoObject * CSharpInstance : : get_mono_object ( ) const {
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND_V ( gchandle . is_released ( ) , nullptr ) ;
2019-12-11 17:08:40 +01:00
return gchandle . get_target ( ) ;
2017-10-02 23:24:00 +02:00
}
2019-02-03 06:35:22 +01:00
Object * CSharpInstance : : get_owner ( ) {
return owner ;
}
2017-10-02 23:24:00 +02:00
bool CSharpInstance : : set ( const StringName & p_name , const Variant & p_value ) {
ERR_FAIL_COND_V ( ! script . is_valid ( ) , false ) ;
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-01-04 21:05:46 +01:00
MonoObject * mono_object = get_mono_object ( ) ;
ERR_FAIL_NULL_V ( mono_object , false ) ;
2017-10-02 23:24:00 +02:00
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
2019-03-08 00:12:19 +01:00
GDMonoField * field = top - > get_field ( p_name ) ;
2017-10-02 23:24:00 +02:00
if ( field ) {
2018-01-04 21:05:46 +01:00
field - > set_value_from_variant ( mono_object , p_value ) ;
return true ;
}
2017-10-02 23:24:00 +02:00
2019-03-08 00:12:19 +01:00
GDMonoProperty * property = top - > get_property ( p_name ) ;
2017-10-02 23:24:00 +02:00
2018-01-04 21:05:46 +01:00
if ( property ) {
2021-10-19 00:26:56 +02:00
property - > set_value_from_variant ( mono_object , p_value ) ;
2017-10-02 23:24:00 +02:00
return true ;
}
top = top - > get_parent_class ( ) ;
}
// Call _set
top = script - > script_class ;
while ( top & & top ! = script - > native ) {
GDMonoMethod * method = top - > get_method ( CACHED_STRING_NAME ( _set ) , 2 ) ;
if ( method ) {
2018-01-04 21:05:46 +01:00
Variant name = p_name ;
const Variant * args [ 2 ] = { & name , & p_value } ;
2017-10-02 23:24:00 +02:00
MonoObject * ret = method - > invoke ( mono_object , args ) ;
2020-07-05 19:19:36 +02:00
if ( ret & & GDMonoMarshal : : unbox < MonoBoolean > ( ret ) ) {
2017-10-02 23:24:00 +02:00
return true ;
2020-07-05 19:19:36 +02:00
}
2018-01-30 18:53:00 +01:00
break ;
2017-10-02 23:24:00 +02:00
}
top = top - > get_parent_class ( ) ;
}
return false ;
}
bool CSharpInstance : : get ( const StringName & p_name , Variant & r_ret ) const {
ERR_FAIL_COND_V ( ! script . is_valid ( ) , false ) ;
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-01-04 21:05:46 +01:00
MonoObject * mono_object = get_mono_object ( ) ;
ERR_FAIL_NULL_V ( mono_object , false ) ;
2017-10-02 23:24:00 +02:00
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
GDMonoField * field = top - > get_field ( p_name ) ;
if ( field ) {
MonoObject * value = field - > get_value ( mono_object ) ;
2018-01-17 20:29:44 +01:00
r_ret = GDMonoMarshal : : mono_object_to_variant ( value ) ;
2017-10-02 23:24:00 +02:00
return true ;
}
2018-01-04 21:05:46 +01:00
GDMonoProperty * property = top - > get_property ( p_name ) ;
if ( property ) {
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2018-01-04 21:05:46 +01:00
MonoObject * value = property - > get_value ( mono_object , & exc ) ;
if ( exc ) {
r_ret = Variant ( ) ;
2018-06-26 21:03:42 +02:00
GDMonoUtils : : set_pending_exception ( exc ) ;
2018-01-04 21:05:46 +01:00
} else {
2018-01-17 20:29:44 +01:00
r_ret = GDMonoMarshal : : mono_object_to_variant ( value ) ;
2018-01-04 21:05:46 +01:00
}
return true ;
}
2017-10-02 23:24:00 +02:00
2018-01-04 21:05:46 +01:00
top = top - > get_parent_class ( ) ;
}
// Call _get
top = script - > script_class ;
while ( top & & top ! = script - > native ) {
2017-10-02 23:24:00 +02:00
GDMonoMethod * method = top - > get_method ( CACHED_STRING_NAME ( _get ) , 1 ) ;
if ( method ) {
Variant name = p_name ;
const Variant * args [ 1 ] = { & name } ;
2018-01-04 21:05:46 +01:00
MonoObject * ret = method - > invoke ( mono_object , args ) ;
2017-10-02 23:24:00 +02:00
if ( ret ) {
r_ret = GDMonoMarshal : : mono_object_to_variant ( ret ) ;
return true ;
}
2018-01-30 18:53:00 +01:00
break ;
2017-10-02 23:24:00 +02:00
}
top = top - > get_parent_class ( ) ;
}
return false ;
}
2020-03-17 07:33:00 +01:00
void CSharpInstance : : get_properties_state_for_reloading ( List < Pair < StringName , Variant > > & r_state ) {
2021-07-26 21:31:17 +02:00
List < PropertyInfo > property_list ;
get_property_list ( & property_list ) ;
2019-07-03 09:44:53 +02:00
2021-07-26 21:31:17 +02:00
for ( const PropertyInfo & prop_info : property_list ) {
2019-07-03 09:44:53 +02:00
Pair < StringName , Variant > state_pair ;
2021-07-26 21:31:17 +02:00
state_pair . first = prop_info . name ;
2019-07-03 09:44:53 +02:00
ManagedType managedType ;
2021-12-28 18:28:01 +01:00
GDMonoField * field = nullptr ;
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
field = top - > get_field ( state_pair . first ) ;
if ( field ) {
break ;
}
top = top - > get_parent_class ( ) ;
}
2020-07-05 19:19:36 +02:00
if ( ! field ) {
2019-07-03 09:44:53 +02:00
continue ; // Properties ignored. We get the property baking fields instead.
2020-07-05 19:19:36 +02:00
}
2019-07-03 09:44:53 +02:00
managedType = field - > get_type ( ) ;
if ( GDMonoMarshal : : managed_to_variant_type ( managedType ) ! = Variant : : NIL ) { // If we can marshal it
if ( get ( state_pair . first , state_pair . second ) ) {
r_state . push_back ( state_pair ) ;
}
}
}
}
2020-03-14 19:20:17 +01:00
void CSharpInstance : : get_event_signals_state_for_reloading ( List < Pair < StringName , Array > > & r_state ) {
MonoObject * owner_managed = get_mono_object ( ) ;
ERR_FAIL_NULL ( owner_managed ) ;
2021-07-26 21:31:17 +02:00
for ( const KeyValue < StringName , CSharpScript : : EventSignal > & E : script - > event_signals ) {
const CSharpScript : : EventSignal & event_signal = E . value ;
2020-03-14 19:20:17 +01:00
MonoDelegate * delegate_field_value = ( MonoDelegate * ) event_signal . field - > get_value ( owner_managed ) ;
2020-07-05 19:19:36 +02:00
if ( ! delegate_field_value ) {
2020-03-14 19:20:17 +01:00
continue ; // Empty
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
Array serialized_data ;
MonoObject * managed_serialized_data = GDMonoMarshal : : variant_to_mono_object ( serialized_data ) ;
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2020-03-14 19:20:17 +01:00
bool success = ( bool ) CACHED_METHOD_THUNK ( DelegateUtils , TrySerializeDelegate ) . invoke ( delegate_field_value , managed_serialized_data , & exc ) ;
if ( exc ) {
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
continue ;
}
if ( success ) {
r_state . push_back ( Pair < StringName , Array > ( event_signal . field - > get_name ( ) , serialized_data ) ) ;
} else if ( OS : : get_singleton ( ) - > is_stdout_verbose ( ) ) {
OS : : get_singleton ( ) - > print ( " Failed to serialize event signal delegate \n " ) ;
}
}
}
2017-10-02 23:24:00 +02:00
void CSharpInstance : : get_property_list ( List < PropertyInfo > * p_properties ) const {
2021-10-30 16:55:21 +02:00
List < PropertyInfo > props ;
2021-10-22 19:25:01 +02:00
for ( OrderedHashMap < StringName , PropertyInfo > : : ConstElement E = script - > member_info . front ( ) ; E ; E = E . next ( ) ) {
2021-10-30 16:55:21 +02:00
props . push_front ( E . value ( ) ) ;
2017-10-02 23:24:00 +02:00
}
2019-03-28 11:01:43 +00:00
// Call _get_property_list
ERR_FAIL_COND ( ! script . is_valid ( ) ) ;
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2019-03-28 11:01:43 +00:00
MonoObject * mono_object = get_mono_object ( ) ;
ERR_FAIL_NULL ( mono_object ) ;
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
GDMonoMethod * method = top - > get_method ( CACHED_STRING_NAME ( _get_property_list ) , 0 ) ;
if ( method ) {
MonoObject * ret = method - > invoke ( mono_object ) ;
if ( ret ) {
Array array = Array ( GDMonoMarshal : : mono_object_to_variant ( ret ) ) ;
2020-07-05 19:19:36 +02:00
for ( int i = 0 , size = array . size ( ) ; i < size ; i + + ) {
2021-10-30 16:55:21 +02:00
props . push_back ( PropertyInfo : : from_dict ( array . get ( i ) ) ) ;
2020-07-05 19:19:36 +02:00
}
2019-03-28 11:01:43 +00:00
}
2021-10-30 16:55:21 +02:00
break ;
2019-03-28 11:01:43 +00:00
}
top = top - > get_parent_class ( ) ;
}
2021-10-30 16:55:21 +02:00
for ( const PropertyInfo & prop : props ) {
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 ;
}
2021-09-12 19:53:34 +02:00
void CSharpInstance : : get_method_list ( List < MethodInfo > * p_list ) const {
2021-10-22 19:25:01 +02:00
if ( ! script - > is_valid ( ) | | ! script - > script_class ) {
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
GD_MONO_SCOPE_THREAD_ATTACH ;
// TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
const Vector < GDMonoMethod * > & methods = top - > get_all_methods ( ) ;
for ( int i = 0 ; i < methods . size ( ) ; + + i ) {
MethodInfo minfo = methods [ i ] - > get_method_info ( ) ;
if ( minfo . name ! = CACHED_STRING_NAME ( dotctor ) ) {
p_list - > push_back ( minfo ) ;
}
}
top = top - > get_parent_class ( ) ;
}
}
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
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2017-10-02 23:24:00 +02:00
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
2018-01-09 17:19:03 +01:00
if ( top - > has_fetched_method_unknown_params ( p_method ) ) {
2017-10-02 23:24:00 +02:00
return true ;
}
top = top - > get_parent_class ( ) ;
}
return false ;
}
2020-02-19 16:27:19 -03:00
Variant CSharpInstance : : call ( 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
GD_MONO_SCOPE_THREAD_ATTACH ;
2017-10-02 23:24:00 +02:00
MonoObject * mono_object = get_mono_object ( ) ;
2018-01-24 01:31:51 +01:00
if ( ! mono_object ) {
2020-02-19 16:27:19 -03:00
r_error . error = Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL ;
2018-01-24 01:31:51 +01:00
ERR_FAIL_V ( Variant ( ) ) ;
}
2017-10-02 23:24:00 +02:00
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
GDMonoMethod * method = top - > get_method ( p_method , p_argcount ) ;
if ( method ) {
MonoObject * return_value = method - > invoke ( mono_object , p_args ) ;
2020-02-19 16:27:19 -03:00
r_error . error = Callable : : CallError : : CALL_OK ;
2018-01-24 01:31:51 +01:00
2017-10-02 23:24:00 +02:00
if ( return_value ) {
2018-01-17 20:29:44 +01:00
return GDMonoMarshal : : mono_object_to_variant ( return_value ) ;
2017-10-02 23:24:00 +02:00
} else {
return Variant ( ) ;
}
}
top = top - > get_parent_class ( ) ;
}
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 ( ) ;
}
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
}
2018-09-12 02:41:54 +02:00
MonoObject * CSharpInstance : : _internal_new_managed ( ) {
2019-02-28 23:12:49 +01:00
// Search the constructor first, to fail with an error if it's not found before allocating anything else.
GDMonoMethod * ctor = script - > script_class - > get_method ( CACHED_STRING_NAME ( dotctor ) , 0 ) ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_NULL_V_MSG ( ctor , nullptr ,
2019-08-09 03:39:45 +02:00
" Cannot create script instance because the class does not define a parameterless constructor: ' " + script - > get_path ( ) + " '. " ) ;
2019-02-28 23:12:49 +01:00
2018-09-12 02:41:54 +02:00
CSharpLanguage : : get_singleton ( ) - > release_script_gchandle ( gchandle ) ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_NULL_V ( owner , nullptr ) ;
ERR_FAIL_COND_V ( script . is_null ( ) , nullptr ) ;
2017-10-02 23:24:00 +02:00
2019-07-03 09:44:53 +02:00
MonoObject * mono_object = mono_object_new ( mono_domain_get ( ) , script - > script_class - > get_mono_ptr ( ) ) ;
2018-09-12 02:41:54 +02:00
if ( ! mono_object ) {
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 > ( ) ;
2019-02-03 06:35:22 +01:00
bool die = _unreference_owner_unsafe ( ) ;
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
2020-07-13 14:13:38 -04:00
CRASH_COND ( die ) ;
2019-02-03 06:35:22 +01:00
2020-04-02 01:20:12 +02:00
owner = nullptr ;
2019-07-03 09:44:53 +02:00
2020-04-02 01:20:12 +02:00
ERR_FAIL_V_MSG ( nullptr , " Failed to allocate memory for the object. " ) ;
2018-09-12 02:41:54 +02:00
}
2019-02-03 06:35:22 +01:00
// Tie managed to unmanaged
2019-12-11 17:08:40 +01:00
gchandle = MonoGCHandleData : : new_strong_handle ( mono_object ) ;
2019-02-03 06:35:22 +01:00
2021-06-04 18:03:15 +02:00
if ( base_ref_counted ) {
2019-02-03 06:35:22 +01:00
_reference_owner_unsafe ( ) ; // Here, after assigning the gchandle (for the refcount_incremented callback)
2020-07-05 19:19:36 +02:00
}
2019-02-03 06:35:22 +01:00
2018-09-12 02:41:54 +02:00
CACHED_FIELD ( GodotObject , ptr ) - > set_value_raw ( mono_object , owner ) ;
// Construct
2020-04-02 01:20:12 +02:00
ctor - > invoke_raw ( mono_object , nullptr ) ;
2018-09-12 02:41:54 +02:00
return mono_object ;
}
2018-09-12 21:08:18 +02:00
void CSharpInstance : : mono_object_disposed ( MonoObject * p_obj ) {
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
CSharpLanguage : : get_singleton ( ) - > release_script_gchandle ( p_obj , gchandle ) ;
}
2019-02-03 06:35:22 +01:00
void CSharpInstance : : mono_object_disposed_baseref ( MonoObject * p_obj , 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 ;
2018-09-12 02:41:54 +02:00
CSharpLanguage : : get_singleton ( ) - > release_script_gchandle ( p_obj , 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 ;
} else if ( ! GDMono : : get_singleton ( ) - > is_finalizing_scripts_domain ( ) ) {
// If the native instance is still alive and this is called from the finalizer,
// then it was referenced from another thread before the finalizer could
// unreference and delete it, so we want to keep it.
// GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this'
// could have already been collected. Instead we will create a new managed instance here.
MonoObject * new_managed = _internal_new_managed ( ) ;
if ( ! new_managed ) {
r_remove_script_instance = true ;
}
2018-09-12 02:41:54 +02:00
}
}
2017-10-02 23:24:00 +02:00
}
2020-03-14 19:20:17 +01:00
void CSharpInstance : : connect_event_signals ( ) {
2021-07-26 21:31:17 +02:00
for ( const KeyValue < StringName , CSharpScript : : EventSignal > & E : script - > event_signals ) {
const CSharpScript : : EventSignal & event_signal = E . value ;
2020-03-14 19:20:17 +01:00
StringName signal_name = event_signal . field - > get_name ( ) ;
// TODO: Use pooling for ManagedCallable instances.
2021-04-25 16:40:58 +02:00
EventSignalCallable * event_signal_callable = memnew ( EventSignalCallable ( owner , & event_signal ) ) ;
2020-03-14 19:20:17 +01:00
2021-03-15 07:08:06 +01:00
Callable callable ( event_signal_callable ) ;
connected_event_signals . push_back ( callable ) ;
owner - > connect ( signal_name , callable ) ;
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
2021-06-04 18:03:15 +02:00
if ( rc_owner - > reference_get_count ( ) > 1 & & gchandle . is_weak ( ) ) { // The managed side also holds a reference, hence 1 instead of 0
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
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.
2019-12-11 17:08:40 +01:00
MonoGCHandleData strong_gchandle = MonoGCHandleData : : new_strong_handle ( gchandle . get_target ( ) ) ;
gchandle . release ( ) ;
gchandle = strong_gchandle ;
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
2021-06-04 18:03:15 +02:00
int refcount = rc_owner - > reference_get_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
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
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.
2019-12-11 17:08:40 +01:00
MonoGCHandleData weak_gchandle = MonoGCHandleData : : new_weak_handle ( gchandle . get_target ( ) ) ;
gchandle . release ( ) ;
gchandle = weak_gchandle ;
2017-10-02 23:24:00 +02:00
return false ;
}
ref_dying = ( refcount = = 0 ) ;
return ref_dying ;
}
2021-09-03 19:40:47 +02:00
const Vector < Multiplayer : : RPCConfig > CSharpInstance : : get_rpc_methods ( ) const {
2020-02-12 11:51:50 +01:00
return script - > get_rpc_methods ( ) ;
}
2018-01-04 21:05:46 +01:00
2017-10-02 23:24:00 +02:00
void CSharpInstance : : notification ( int p_notification ) {
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2017-12-31 22:37:57 +01:00
if ( p_notification = = Object : : NOTIFICATION_PREDELETE ) {
2018-09-12 02:41:54 +02:00
// When NOTIFICATION_PREDELETE is sent, we also take the chance to call Dispose().
// It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed
// 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 ) {
// It's not safe to proceed if the owner derives RefCounted and the refcount reached 0.
2018-09-12 02:41:54 +02:00
// At this point, Dispose() was already called (manually or from the finalizer) so
// that's not a problem. The refcount wouldn't have reached 0 otherwise, since the
// managed side references it and Dispose() needs to be called to release it.
2021-06-04 18:03:15 +02:00
// However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but
2018-09-12 02:41:54 +02:00
// this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784
return ;
2017-12-31 22:37:57 +01:00
}
2018-09-12 02:41:54 +02:00
_call_notification ( p_notification ) ;
MonoObject * mono_object = get_mono_object ( ) ;
ERR_FAIL_NULL ( mono_object ) ;
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2018-11-30 20:43:06 +01:00
GDMonoUtils : : dispose ( mono_object , & exc ) ;
2018-09-12 02:41:54 +02:00
if ( exc ) {
GDMonoUtils : : set_pending_exception ( exc ) ;
}
2017-12-31 22:37:57 +01:00
return ;
}
2018-09-12 02:41:54 +02:00
_call_notification ( p_notification ) ;
2017-12-31 22:37:57 +01:00
}
2018-09-12 02:41:54 +02:00
void CSharpInstance : : _call_notification ( int p_notification ) {
2020-01-16 17:11:13 +01:00
GD_MONO_ASSERT_THREAD_ATTACHED ;
2018-09-12 02:41:54 +02:00
MonoObject * mono_object = get_mono_object ( ) ;
ERR_FAIL_NULL ( mono_object ) ;
// Custom version of _call_multilevel, optimized for _notification
2020-07-05 19:19:36 +02:00
int32_t arg = p_notification ;
2018-09-12 02:41:54 +02:00
void * args [ 1 ] = { & arg } ;
StringName method_name = CACHED_STRING_NAME ( _notification ) ;
GDMonoClass * top = script - > script_class ;
while ( top & & top ! = script - > native ) {
GDMonoMethod * method = top - > get_method ( method_name , 1 ) ;
if ( method ) {
method - > invoke_raw ( mono_object , args ) ;
return ;
}
2017-10-02 23:24:00 +02:00
2018-09-12 02:41:54 +02:00
top = top - > get_parent_class ( ) ;
}
2017-10-02 23:24:00 +02:00
}
2019-05-24 00:40:16 +02:00
String CSharpInstance : : to_string ( bool * r_valid ) {
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2019-05-24 00:40:16 +02:00
MonoObject * mono_object = get_mono_object ( ) ;
2020-04-02 01:20:12 +02:00
if ( mono_object = = nullptr ) {
2020-07-05 19:19:36 +02:00
if ( r_valid ) {
2019-05-24 00:40:16 +02:00
* r_valid = false ;
2020-07-05 19:19:36 +02:00
}
2019-05-24 00:40:16 +02:00
return String ( ) ;
}
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2019-05-24 00:40:16 +02:00
MonoString * result = GDMonoUtils : : object_to_string ( mono_object , & exc ) ;
if ( exc ) {
GDMonoUtils : : set_pending_exception ( exc ) ;
2020-07-05 19:19:36 +02:00
if ( r_valid ) {
2019-05-24 00:40:16 +02:00
* r_valid = false ;
2020-07-05 19:19:36 +02:00
}
2019-05-24 00:40:16 +02:00
return String ( ) ;
}
2020-04-02 01:20:12 +02:00
if ( result = = nullptr ) {
2020-07-05 19:19:36 +02:00
if ( r_valid ) {
2019-05-24 00:40:16 +02:00
* r_valid = false ;
2020-07-05 19:19:36 +02:00
}
2019-05-24 00:40:16 +02:00
return String ( ) ;
}
return GDMonoMarshal : : mono_string_to_godot ( result ) ;
}
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 ( ) {
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
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.
2019-12-11 17:08:40 +01:00
MonoObject * mono_object = gchandle . get_target ( ) ;
2018-11-30 20:43:06 +01:00
if ( mono_object ) {
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2018-11-30 20:43:06 +01:00
GDMonoUtils : : dispose ( mono_object , & exc ) ;
if ( exc ) {
GDMonoUtils : : set_pending_exception ( exc ) ;
}
}
}
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 ) ;
2019-02-03 06:35:22 +01:00
CSharpScriptBinding & script_binding = ( ( Map < 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
2021-06-04 18:03:15 +02:00
CRASH_COND ( rc_owner - > reference_get_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
Set < Object * > : : Element * match = script - > instances . find ( owner ) ;
CRASH_COND ( ! match ) ;
script - > instances . erase ( match ) ;
# else
script - > instances . erase ( owner ) ;
# endif
}
}
# ifdef TOOLS_ENABLED
void CSharpScript : : _placeholder_erased ( PlaceHolderScriptInstance * p_placeholder ) {
placeholders . erase ( p_placeholder ) ;
}
# endif
# ifdef TOOLS_ENABLED
void CSharpScript : : _update_exports_values ( Map < StringName , Variant > & values , List < PropertyInfo > & propnames ) {
if ( base_cache . is_valid ( ) ) {
base_cache - > _update_exports_values ( values , 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
}
}
2019-07-03 09:44:53 +02:00
void CSharpScript : : _update_member_info_no_exports ( ) {
if ( exports_invalidated ) {
2020-01-16 17:11:13 +01:00
GD_MONO_ASSERT_THREAD_ATTACHED ;
2019-07-03 09:44:53 +02:00
exports_invalidated = false ;
member_info . clear ( ) ;
GDMonoClass * top = script_class ;
while ( top & & top ! = native ) {
PropertyInfo prop_info ;
bool exported ;
const Vector < GDMonoField * > & fields = top - > get_all_fields ( ) ;
for ( int i = fields . size ( ) - 1 ; i > = 0 ; i - - ) {
GDMonoField * field = fields [ i ] ;
if ( _get_member_export ( field , /* inspect export: */ false , prop_info , exported ) ) {
StringName member_name = field - > get_name ( ) ;
member_info [ member_name ] = prop_info ;
exported_members_cache . push_front ( prop_info ) ;
exported_members_defval_cache [ member_name ] = Variant ( ) ;
}
}
const Vector < GDMonoProperty * > & properties = top - > get_all_properties ( ) ;
for ( int i = properties . size ( ) - 1 ; i > = 0 ; i - - ) {
GDMonoProperty * property = properties [ i ] ;
if ( _get_member_export ( property , /* inspect export: */ false , prop_info , exported ) ) {
StringName member_name = property - > get_name ( ) ;
member_info [ member_name ] = prop_info ;
exported_members_cache . push_front ( prop_info ) ;
exported_members_defval_cache [ member_name ] = Variant ( ) ;
}
}
top = top - > get_parent_class ( ) ;
}
}
}
2019-07-08 15:22:51 +02:00
# endif
2019-07-03 09:44:53 +02:00
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
{
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2017-10-02 23:24:00 +02:00
changed = true ;
member_info . clear ( ) ;
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
MonoObject * tmp_object = nullptr ;
Object * tmp_native = nullptr ;
uint32_t tmp_pinned_gchandle = 0 ;
2017-10-02 23:24:00 +02:00
2020-05-11 03:20:11 +09:00
if ( is_editor ) {
exports_invalidated = false ;
2017-10-02 23:24:00 +02:00
2020-05-11 03:20:11 +09:00
exported_members_cache . clear ( ) ;
exported_members_defval_cache . clear ( ) ;
2017-10-02 23:24:00 +02:00
2020-05-11 03:20:11 +09:00
// Here we create a temporary managed instance of the class to get the initial values
tmp_object = mono_object_new ( mono_domain_get ( ) , script_class - > get_mono_ptr ( ) ) ;
2017-10-02 23:24:00 +02:00
2020-05-11 03:20:11 +09:00
if ( ! tmp_object ) {
ERR_PRINT ( " Failed to allocate temporary MonoObject. " ) ;
return false ;
}
2019-02-28 23:12:49 +01:00
2020-05-11 03:20:11 +09:00
tmp_pinned_gchandle = GDMonoUtils : : new_strong_gchandle_pinned ( tmp_object ) ; // pin it (not sure if needed)
2019-02-28 23:12:49 +01:00
2020-05-11 03:20:11 +09:00
GDMonoMethod * ctor = script_class - > get_method ( CACHED_STRING_NAME ( dotctor ) , 0 ) ;
2018-09-12 02:41:54 +02:00
2020-05-11 03:20:11 +09:00
ERR_FAIL_NULL_V_MSG ( ctor , false ,
" Cannot construct temporary MonoObject because the class does not define a parameterless constructor: ' " + get_path ( ) + " '. " ) ;
2020-01-13 20:58:46 +01:00
2020-05-11 03:20:11 +09:00
MonoException * ctor_exc = nullptr ;
ctor - > invoke ( tmp_object , nullptr , & ctor_exc ) ;
2020-01-13 20:58:46 +01:00
2020-05-11 03:20:11 +09:00
tmp_native = GDMonoMarshal : : unbox < Object * > ( CACHED_FIELD ( GodotObject , ptr ) - > get_value ( tmp_object ) ) ;
2018-09-12 02:41:54 +02:00
2020-05-11 03:20:11 +09:00
if ( ctor_exc ) {
// TODO: Should we free 'tmp_native' if the exception was thrown after its creation?
GDMonoUtils : : free_gchandle ( tmp_pinned_gchandle ) ;
tmp_object = nullptr ;
ERR_PRINT ( " Exception thrown from constructor of temporary MonoObject: " ) ;
GDMonoUtils : : debug_print_unhandled_exception ( ctor_exc ) ;
return false ;
}
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
2017-10-04 22:49:59 -07:00
GDMonoClass * top = script_class ;
2017-10-02 23:24:00 +02:00
2017-10-04 22:49:59 -07:00
while ( top & & top ! = native ) {
2018-01-04 21:05:46 +01:00
PropertyInfo prop_info ;
bool exported ;
2017-10-04 22:49:59 -07:00
const Vector < GDMonoField * > & fields = top - > get_all_fields ( ) ;
2017-12-02 21:01:58 -05:00
for ( int i = fields . size ( ) - 1 ; i > = 0 ; i - - ) {
2017-10-04 22:49:59 -07:00
GDMonoField * field = fields [ i ] ;
2019-07-03 09:44:53 +02:00
if ( _get_member_export ( field , /* inspect export: */ true , prop_info , exported ) ) {
StringName member_name = field - > get_name ( ) ;
2017-10-02 23:24:00 +02:00
2020-05-11 03:20:11 +09:00
member_info [ member_name ] = prop_info ;
2020-05-22 00:58:34 +02:00
if ( exported ) {
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2020-05-22 00:58:34 +02:00
if ( is_editor ) {
exported_members_cache . push_front ( prop_info ) ;
2017-10-02 23:24:00 +02:00
2020-05-22 00:58:34 +02:00
if ( tmp_object ) {
exported_members_defval_cache [ member_name ] = GDMonoMarshal : : mono_object_to_variant ( field - > get_value ( tmp_object ) ) ;
}
2018-01-04 21:05:46 +01:00
}
2020-05-11 03:20:11 +09:00
# endif
2020-05-22 00:58:34 +02:00
# if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
exported_members_names . insert ( member_name ) ;
# endif
}
2018-01-04 21:05:46 +01:00
}
}
2017-10-02 23:24:00 +02:00
2018-01-04 21:05:46 +01:00
const Vector < GDMonoProperty * > & properties = top - > get_all_properties ( ) ;
2017-10-02 23:24:00 +02:00
2018-01-04 21:05:46 +01:00
for ( int i = properties . size ( ) - 1 ; i > = 0 ; i - - ) {
GDMonoProperty * property = properties [ i ] ;
2017-10-17 14:02:19 +02:00
2019-07-03 09:44:53 +02:00
if ( _get_member_export ( property , /* inspect export: */ true , prop_info , exported ) ) {
StringName member_name = property - > get_name ( ) ;
2017-10-17 14:02:19 +02:00
2020-05-11 03:20:11 +09:00
member_info [ member_name ] = prop_info ;
2020-05-22 00:58:34 +02:00
if ( exported ) {
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2020-05-22 00:58:34 +02:00
if ( is_editor ) {
exported_members_cache . push_front ( prop_info ) ;
if ( tmp_object ) {
MonoException * exc = nullptr ;
MonoObject * ret = property - > get_value ( tmp_object , & exc ) ;
if ( exc ) {
exported_members_defval_cache [ member_name ] = Variant ( ) ;
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
} else {
exported_members_defval_cache [ member_name ] = GDMonoMarshal : : mono_object_to_variant ( ret ) ;
}
2018-01-04 21:05:46 +01:00
}
2017-10-17 14:02:19 +02:00
}
2020-05-11 03:20:11 +09:00
# endif
2020-05-22 00:58:34 +02:00
# if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
exported_members_names . insert ( member_name ) ;
# endif
}
2017-10-02 23:24:00 +02:00
}
}
2017-10-04 22:49:59 -07:00
top = top - > get_parent_class ( ) ;
2017-10-02 23:24:00 +02:00
}
2018-09-12 02:41:54 +02:00
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
if ( is_editor ) {
// Need to check this here, before disposal
2021-06-04 18:03:15 +02:00
bool base_ref_counted = Object : : cast_to < RefCounted > ( tmp_native ) ! = nullptr ;
2020-01-24 18:27:45 +01:00
2020-05-11 03:20:11 +09:00
// Dispose the temporary managed instance
2018-09-12 02:41:54 +02:00
2020-05-11 03:20:11 +09:00
MonoException * exc = nullptr ;
GDMonoUtils : : dispose ( tmp_object , & exc ) ;
2018-09-12 02:41:54 +02:00
2020-05-11 03:20:11 +09:00
if ( exc ) {
ERR_PRINT ( " Exception thrown from method Dispose() of temporary MonoObject: " ) ;
GDMonoUtils : : debug_print_unhandled_exception ( exc ) ;
}
2018-09-12 02:41:54 +02:00
2020-05-11 03:20:11 +09:00
GDMonoUtils : : free_gchandle ( tmp_pinned_gchandle ) ;
tmp_object = nullptr ;
2020-01-13 20:58:46 +01:00
2021-06-04 18:03:15 +02:00
if ( tmp_native & & ! base_ref_counted ) {
2020-05-11 03:20:11 +09:00
Node * node = Object : : cast_to < Node > ( tmp_native ) ;
if ( node & & node - > is_inside_tree ( ) ) {
ERR_PRINT ( " Temporary instance was added to the scene tree. " ) ;
} else {
memdelete ( tmp_native ) ;
}
2020-01-13 20:58:46 +01:00
}
}
2020-05-11 03:20:11 +09:00
# endif
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
Map < StringName , Variant > values ;
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 ) {
2021-07-26 21:31:17 +02:00
for ( PlaceHolderScriptInstance * & script_instance : placeholders ) {
script_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 ;
}
2018-06-30 20:14:40 -05:00
void CSharpScript : : load_script_signals ( GDMonoClass * p_class , GDMonoClass * p_native_class ) {
// no need to load the script's signals more than once
if ( ! signals_invalidated ) {
return ;
}
2018-01-18 18:17:29 +01:00
2018-06-30 20:14:40 -05:00
// make sure this classes signals are empty when loading for the first time
_signals . clear ( ) ;
2020-03-14 19:20:17 +01:00
event_signals . clear ( ) ;
2018-01-18 18:17:29 +01:00
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-06-30 20:14:40 -05:00
GDMonoClass * top = p_class ;
while ( top & & top ! = p_native_class ) {
const Vector < GDMonoClass * > & delegates = top - > get_all_delegates ( ) ;
for ( int i = delegates . size ( ) - 1 ; i > = 0 ; - - i ) {
GDMonoClass * delegate = delegates [ i ] ;
2018-01-18 18:17:29 +01:00
2020-07-05 19:19:36 +02:00
if ( ! delegate - > has_attribute ( CACHED_CLASS ( SignalAttribute ) ) ) {
2020-03-14 19:20:17 +01:00
continue ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
// Arguments are accessibles as arguments of .Invoke method
GDMonoMethod * invoke_method = delegate - > get_method ( mono_get_delegate_invoke ( delegate - > get_mono_ptr ( ) ) ) ;
Vector < SignalParameter > parameters ;
if ( _get_signal ( top , invoke_method , parameters ) ) {
2018-06-30 20:14:40 -05:00
_signals [ delegate - > get_name ( ) ] = parameters ;
2018-01-18 18:17:29 +01:00
}
}
2018-06-30 20:14:40 -05:00
2020-03-14 19:20:17 +01:00
List < StringName > found_event_signals ;
2020-04-02 01:20:12 +02:00
void * iter = nullptr ;
MonoEvent * raw_event = nullptr ;
while ( ( raw_event = mono_class_get_events ( top - > get_mono_ptr ( ) , & iter ) ) ! = nullptr ) {
2020-03-14 19:20:17 +01:00
MonoCustomAttrInfo * event_attrs = mono_custom_attrs_from_event ( top - > get_mono_ptr ( ) , raw_event ) ;
if ( event_attrs ) {
if ( mono_custom_attrs_has_attr ( event_attrs , CACHED_CLASS ( SignalAttribute ) - > get_mono_ptr ( ) ) ) {
2021-01-18 16:34:10 +08:00
String event_name = String : : utf8 ( mono_event_get_name ( raw_event ) ) ;
2020-03-14 19:20:17 +01:00
found_event_signals . push_back ( StringName ( event_name ) ) ;
}
mono_custom_attrs_free ( event_attrs ) ;
}
}
const Vector < GDMonoField * > & fields = top - > get_all_fields ( ) ;
for ( int i = 0 ; i < fields . size ( ) ; i + + ) {
GDMonoField * field = fields [ i ] ;
GDMonoClass * field_class = field - > get_type ( ) . type_class ;
2020-07-05 19:19:36 +02:00
if ( ! mono_class_is_delegate ( field_class - > get_mono_ptr ( ) ) ) {
2020-03-14 19:20:17 +01:00
continue ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
2020-07-05 19:19:36 +02:00
if ( ! found_event_signals . find ( field - > get_name ( ) ) ) {
2020-03-14 19:20:17 +01:00
continue ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
GDMonoMethod * invoke_method = field_class - > get_method ( mono_get_delegate_invoke ( field_class - > get_mono_ptr ( ) ) ) ;
Vector < SignalParameter > parameters ;
if ( _get_signal ( top , invoke_method , parameters ) ) {
event_signals [ field - > get_name ( ) ] = { field , invoke_method , parameters } ;
}
}
2018-06-30 20:14:40 -05:00
top = top - > get_parent_class ( ) ;
2018-01-18 18:17:29 +01:00
}
2018-06-30 20:14:40 -05:00
signals_invalidated = false ;
2018-01-18 18:17:29 +01:00
}
2020-03-14 19:20:17 +01:00
bool CSharpScript : : _get_signal ( GDMonoClass * p_class , GDMonoMethod * p_delegate_invoke , Vector < SignalParameter > & params ) {
2020-01-16 17:11:13 +01:00
GD_MONO_ASSERT_THREAD_ATTACHED ;
2020-03-14 19:20:17 +01:00
Vector < StringName > names ;
Vector < ManagedType > types ;
p_delegate_invoke - > get_parameter_names ( names ) ;
p_delegate_invoke - > get_parameter_types ( types ) ;
2018-01-18 23:27:43 +01:00
2020-03-14 19:20:17 +01:00
for ( int i = 0 ; i < names . size ( ) ; + + i ) {
SignalParameter arg ;
arg . name = names [ i ] ;
2018-01-18 23:27:43 +01:00
2020-03-14 19:20:17 +01:00
bool nil_is_variant = false ;
arg . type = GDMonoMarshal : : managed_to_variant_type ( types [ i ] , & nil_is_variant ) ;
2018-01-18 23:27:43 +01:00
2020-03-14 19:20:17 +01:00
if ( arg . type = = Variant : : NIL ) {
if ( nil_is_variant ) {
arg . nil_is_variant = true ;
} else {
ERR_PRINT ( " Unknown type of signal parameter: ' " + arg . name + " ' in ' " + p_class - > get_full_name ( ) + " '. " ) ;
return false ;
2018-01-18 23:27:43 +01:00
}
}
2020-03-14 19:20:17 +01:00
params . push_back ( arg ) ;
2018-01-18 23:27:43 +01:00
}
2020-03-14 19:20:17 +01:00
return true ;
2018-01-18 23:27:43 +01:00
}
2018-09-12 18:54:20 +02:00
/**
* Returns false if there was an error, otherwise true.
* If there was an error, r_prop_info and r_exported are not assigned any value.
*/
2019-07-03 09:44:53 +02:00
bool CSharpScript : : _get_member_export ( IMonoClassMember * p_member , bool p_inspect_export , PropertyInfo & r_prop_info , bool & r_exported ) {
2020-01-16 17:11:13 +01:00
GD_MONO_ASSERT_THREAD_ATTACHED ;
2019-05-18 04:14:21 +02:00
// Goddammit, C++. All I wanted was some nested functions.
# define MEMBER_FULL_QUALIFIED_NAME(m_member) \
(m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name())
2018-01-04 21:05:46 +01:00
if ( p_member - > is_static ( ) ) {
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2020-07-05 19:19:36 +02:00
if ( p_member - > has_attribute ( CACHED_CLASS ( ExportAttribute ) ) ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Cannot export member because it is static: ' " + MEMBER_FULL_QUALIFIED_NAME ( p_member ) + " '. " ) ;
2020-07-05 19:19:36 +02:00
}
2020-05-11 03:20:11 +09:00
# endif
2018-01-04 21:05:46 +01:00
return false ;
}
2020-07-05 19:19:36 +02:00
if ( member_info . has ( p_member - > get_name ( ) ) ) {
2018-01-04 21:05:46 +01:00
return false ;
2020-07-05 19:19:36 +02:00
}
2018-01-04 21:05:46 +01:00
ManagedType type ;
2019-01-29 00:02:35 +01:00
if ( p_member - > get_member_type ( ) = = IMonoClassMember : : MEMBER_TYPE_FIELD ) {
2018-01-04 21:05:46 +01:00
type = static_cast < GDMonoField * > ( p_member ) - > get_type ( ) ;
2019-01-29 00:02:35 +01:00
} else if ( p_member - > get_member_type ( ) = = IMonoClassMember : : MEMBER_TYPE_PROPERTY ) {
2018-01-04 21:05:46 +01:00
type = static_cast < GDMonoProperty * > ( p_member ) - > get_type ( ) ;
} else {
CRASH_NOW ( ) ;
}
2019-07-03 09:44:53 +02:00
bool exported = p_member - > has_attribute ( CACHED_CLASS ( ExportAttribute ) ) ;
2018-09-12 18:54:20 +02:00
2019-01-29 00:02:35 +01:00
if ( p_member - > get_member_type ( ) = = IMonoClassMember : : MEMBER_TYPE_PROPERTY ) {
2018-09-12 18:54:20 +02:00
GDMonoProperty * property = static_cast < GDMonoProperty * > ( p_member ) ;
2019-05-18 04:14:21 +02:00
if ( ! property - > has_getter ( ) ) {
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2020-07-05 19:19:36 +02:00
if ( exported ) {
2020-09-29 12:56:28 -03:00
ERR_PRINT ( " Cannot export a property without a getter: ' " + MEMBER_FULL_QUALIFIED_NAME ( p_member ) + " '. " ) ;
2020-07-05 19:19:36 +02:00
}
2020-05-11 03:20:11 +09:00
# endif
2019-05-18 04:14:21 +02:00
return false ;
}
if ( ! property - > has_setter ( ) ) {
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2020-07-05 19:19:36 +02:00
if ( exported ) {
2020-09-29 12:56:28 -03:00
ERR_PRINT ( " Cannot export a property without a setter: ' " + MEMBER_FULL_QUALIFIED_NAME ( p_member ) + " '. " ) ;
2020-07-05 19:19:36 +02:00
}
2020-05-11 03:20:11 +09:00
# endif
2018-09-12 18:54:20 +02:00
return false ;
2018-01-04 21:05:46 +01:00
}
2018-09-12 18:54:20 +02:00
}
2018-01-04 21:05:46 +01:00
2020-03-14 19:20:17 +01:00
bool nil_is_variant = false ;
Variant : : Type variant_type = GDMonoMarshal : : managed_to_variant_type ( type , & nil_is_variant ) ;
2019-07-03 09:44:53 +02:00
if ( ! p_inspect_export | | ! exported ) {
r_prop_info = PropertyInfo ( variant_type , ( String ) p_member - > get_name ( ) , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_SCRIPT_VARIABLE ) ;
r_exported = false ;
return true ;
}
2020-05-30 12:21:24 +07:00
# ifdef TOOLS_ENABLED
2018-09-12 18:54:20 +02:00
MonoObject * attr = p_member - > get_attribute ( CACHED_CLASS ( ExportAttribute ) ) ;
2020-05-30 12:21:24 +07:00
# endif
2018-01-04 21:05:46 +01:00
2018-09-12 18:54:20 +02:00
PropertyHint hint = PROPERTY_HINT_NONE ;
String hint_string ;
2018-01-04 21:05:46 +01:00
2020-03-14 19:20:17 +01:00
if ( variant_type = = Variant : : NIL & & ! nil_is_variant ) {
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Unknown exported member type: ' " + MEMBER_FULL_QUALIFIED_NAME ( p_member ) + " '. " ) ;
2020-05-11 03:20:11 +09:00
# endif
2018-09-12 18:54:20 +02:00
return false ;
2019-05-18 04:14:21 +02:00
}
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2019-05-18 04:14:21 +02:00
int hint_res = _try_get_member_export_hint ( p_member , type , variant_type , /* allow_generics: */ true , hint , hint_string ) ;
2019-08-09 03:39:45 +02:00
ERR_FAIL_COND_V_MSG ( hint_res = = - 1 , false ,
" Error while trying to determine information about the exported member: ' " +
MEMBER_FULL_QUALIFIED_NAME ( p_member ) + " '. " ) ;
2019-05-18 04:14:21 +02:00
if ( hint_res = = 0 ) {
hint = PropertyHint ( CACHED_FIELD ( ExportAttribute , hint ) - > get_int_value ( attr ) ) ;
hint_string = CACHED_FIELD ( ExportAttribute , hintString ) - > get_string_value ( attr ) ;
}
2020-05-11 03:20:11 +09:00
# endif
2019-05-18 04:14:21 +02:00
2020-03-14 19:20:17 +01:00
uint32_t prop_usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE ;
if ( variant_type = = Variant : : NIL ) {
// System.Object (Variant)
prop_usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
}
r_prop_info = PropertyInfo ( variant_type , ( String ) p_member - > get_name ( ) , hint , hint_string , prop_usage ) ;
2019-05-18 04:14:21 +02:00
r_exported = true ;
return true ;
# undef MEMBER_FULL_QUALIFIED_NAME
}
2020-05-11 03:20:11 +09:00
# ifdef TOOLS_ENABLED
2019-05-18 04:14:21 +02:00
int CSharpScript : : _try_get_member_export_hint ( IMonoClassMember * p_member , ManagedType p_type , Variant : : Type p_variant_type , bool p_allow_generics , PropertyHint & r_hint , String & r_hint_string ) {
2020-03-14 19:20:17 +01:00
if ( p_variant_type = = Variant : : NIL ) {
// System.Object (Variant)
return 1 ;
}
2020-01-16 17:11:13 +01:00
GD_MONO_ASSERT_THREAD_ATTACHED ;
2019-05-18 04:14:21 +02:00
if ( p_variant_type = = Variant : : INT & & p_type . type_encoding = = MONO_TYPE_VALUETYPE & & mono_class_is_enum ( p_type . type_class - > get_mono_ptr ( ) ) ) {
r_hint = PROPERTY_HINT_ENUM ;
2018-09-12 18:54:20 +02:00
2019-05-18 04:14:21 +02:00
Vector < MonoClassField * > fields = p_type . type_class - > get_enum_fields ( ) ;
2018-01-04 21:05:46 +01:00
2019-05-18 04:14:21 +02:00
MonoType * enum_basetype = mono_class_enum_basetype ( p_type . type_class - > get_mono_ptr ( ) ) ;
2018-01-04 21:05:46 +01:00
2018-09-12 18:54:20 +02:00
String name_only_hint_string ;
// True: enum Foo { Bar, Baz, Quux }
// True: enum Foo { Bar = 0, Baz = 1, Quux = 2 }
// False: enum Foo { Bar = 0, Baz = 7, Quux = 5 }
bool uses_default_values = true ;
for ( int i = 0 ; i < fields . size ( ) ; i + + ) {
MonoClassField * field = fields [ i ] ;
if ( i > 0 ) {
2019-05-18 04:14:21 +02:00
r_hint_string + = " , " ;
2018-09-12 18:54:20 +02:00
name_only_hint_string + = " , " ;
2018-01-04 21:05:46 +01:00
}
2018-09-12 18:54:20 +02:00
2021-01-18 16:34:10 +08:00
String enum_field_name = String : : utf8 ( mono_field_get_name ( field ) ) ;
2019-05-18 04:14:21 +02:00
r_hint_string + = enum_field_name ;
2018-09-12 18:54:20 +02:00
name_only_hint_string + = enum_field_name ;
// TODO:
// Instead of using mono_field_get_value_object, we can do this without boxing. Check the
// internal mono functions: ves_icall_System_Enum_GetEnumValuesAndNames and the get_enum_field.
2020-04-02 01:20:12 +02:00
MonoObject * val_obj = mono_field_get_value_object ( mono_domain_get ( ) , field , nullptr ) ;
2018-09-12 18:54:20 +02:00
2019-08-09 03:39:45 +02:00
ERR_FAIL_NULL_V_MSG ( val_obj , - 1 , " Failed to get ' " + enum_field_name + " ' constant enum value. " ) ;
2018-09-12 18:54:20 +02:00
bool r_error ;
uint64_t val = GDMonoUtils : : unbox_enum_value ( val_obj , enum_basetype , r_error ) ;
2019-08-09 03:39:45 +02:00
ERR_FAIL_COND_V_MSG ( r_error , - 1 , " Failed to unbox ' " + enum_field_name + " ' constant enum value. " ) ;
2018-09-12 18:54:20 +02:00
2019-02-21 20:57:39 +01:00
if ( val ! = ( unsigned int ) i ) {
2018-09-12 18:54:20 +02:00
uses_default_values = false ;
}
2019-05-18 04:14:21 +02:00
r_hint_string + = " : " ;
r_hint_string + = String : : num_uint64 ( val ) ;
2018-01-04 21:05:46 +01:00
}
2018-09-12 18:54:20 +02:00
if ( uses_default_values ) {
// If we use the format NAME:VAL, that's what the editor displays.
// That's annoying if the user is not using custom values for the enum constants.
// This may not be needed in the future if the editor is changed to not display values.
2019-05-18 04:14:21 +02:00
r_hint_string = name_only_hint_string ;
2018-09-12 18:54:20 +02:00
}
2019-05-18 04:14:21 +02:00
} else if ( p_variant_type = = Variant : : OBJECT & & CACHED_CLASS ( GodotResource ) - > is_assignable_from ( p_type . type_class ) ) {
GDMonoClass * field_native_class = GDMonoUtils : : get_class_native_base ( p_type . type_class ) ;
2020-04-02 01:20:12 +02:00
CRASH_COND ( field_native_class = = nullptr ) ;
2019-03-07 22:32:08 +01:00
2019-05-18 04:14:21 +02:00
r_hint = PROPERTY_HINT_RESOURCE_TYPE ;
2020-03-14 19:20:17 +01:00
r_hint_string = String ( NATIVE_GDMONOCLASS_NAME ( field_native_class ) ) ;
2019-05-18 04:14:21 +02:00
} else if ( p_allow_generics & & p_variant_type = = Variant : : ARRAY ) {
// Nested arrays are not supported in the inspector
ManagedType elem_type ;
2020-07-05 19:19:36 +02:00
if ( ! GDMonoMarshal : : try_get_array_element_type ( p_type , elem_type ) ) {
2019-05-18 04:14:21 +02:00
return 0 ;
2020-07-05 19:19:36 +02:00
}
2019-05-18 04:14:21 +02:00
Variant : : Type elem_variant_type = GDMonoMarshal : : managed_to_variant_type ( elem_type ) ;
PropertyHint elem_hint = PROPERTY_HINT_NONE ;
String elem_hint_string ;
2019-08-09 03:39:45 +02:00
ERR_FAIL_COND_V_MSG ( elem_variant_type = = Variant : : NIL , - 1 , " Unknown array element type. " ) ;
2019-05-18 04:14:21 +02:00
2021-08-20 18:35:04 +08:00
bool preset_hint = false ;
if ( elem_variant_type = = Variant : : STRING ) {
MonoObject * attr = p_member - > get_attribute ( CACHED_CLASS ( ExportAttribute ) ) ;
if ( PropertyHint ( CACHED_FIELD ( ExportAttribute , hint ) - > get_int_value ( attr ) ) = = PROPERTY_HINT_ENUM ) {
r_hint_string = itos ( elem_variant_type ) + " / " + itos ( PROPERTY_HINT_ENUM ) + " : " + CACHED_FIELD ( ExportAttribute , hintString ) - > get_string_value ( attr ) ;
preset_hint = true ;
}
}
2019-05-18 04:14:21 +02:00
2021-08-20 18:35:04 +08:00
if ( ! preset_hint ) {
int hint_res = _try_get_member_export_hint ( p_member , elem_type , elem_variant_type , /* allow_generics: */ false , elem_hint , elem_hint_string ) ;
2019-05-18 04:14:21 +02:00
2021-08-20 18:35:04 +08:00
ERR_FAIL_COND_V_MSG ( hint_res = = - 1 , - 1 , " Error while trying to determine information about the array element type. " ) ;
2019-05-18 04:14:21 +02:00
2021-08-18 20:30:51 +08:00
// Format: type/hint:hint_string
r_hint_string = itos ( elem_variant_type ) + " / " + itos ( elem_hint ) + " : " + elem_hint_string ;
}
2019-05-18 04:14:21 +02:00
r_hint = PROPERTY_HINT_TYPE_STRING ;
} else if ( p_allow_generics & & p_variant_type = = Variant : : DICTIONARY ) {
// TODO: Dictionaries are not supported in the inspector
2018-01-04 21:05:46 +01:00
} else {
2019-05-18 04:14:21 +02:00
return 0 ;
2018-01-04 21:05:46 +01:00
}
2019-05-18 04:14:21 +02:00
return 1 ;
2018-01-04 21:05:46 +01:00
}
2018-01-27 18:44:04 +01:00
# endif
2018-01-04 21:05:46 +01:00
2020-02-19 16:27:19 -03:00
Variant CSharpScript : : call ( const StringName & p_method , const Variant * * p_args , int p_argcount , Callable : : CallError & r_error ) {
2020-04-02 01:20:12 +02:00
if ( unlikely ( GDMono : : get_singleton ( ) = = nullptr ) ) {
2018-05-11 13:50:56 +02:00
// Probably not the best error but eh.
2020-02-19 16:27:19 -03:00
r_error . error = Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL ;
2018-05-11 13:50:56 +02:00
return Variant ( ) ;
}
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2017-10-02 23:24:00 +02:00
GDMonoClass * top = script_class ;
while ( top & & top ! = native ) {
GDMonoMethod * method = top - > get_method ( p_method , p_argcount ) ;
if ( method & & method - > is_static ( ) ) {
2020-04-02 01:20:12 +02:00
MonoObject * result = method - > invoke ( nullptr , p_args ) ;
2017-10-02 23:24:00 +02:00
if ( result ) {
2018-01-17 20:29:44 +01:00
return GDMonoMarshal : : mono_object_to_variant ( result ) ;
2017-10-02 23:24:00 +02:00
} else {
return Variant ( ) ;
}
}
top = top - > get_parent_class ( ) ;
}
// No static method found. Try regular instance calls
return Script : : call ( p_method , p_args , p_argcount , r_error ) ;
}
void CSharpScript : : _resource_path_changed ( ) {
2020-07-05 19:19:36 +02:00
_update_name ( ) ;
2017-10-02 23:24:00 +02:00
}
2017-10-30 22:17:20 +01:00
bool CSharpScript : : _get ( const StringName & p_name , Variant & r_ret ) const {
if ( p_name = = CSharpLanguage : : singleton - > string_names . _script_source ) {
r_ret = get_source_code ( ) ;
return true ;
}
return false ;
}
bool CSharpScript : : _set ( const StringName & p_name , const Variant & p_value ) {
if ( p_name = = CSharpLanguage : : singleton - > string_names . _script_source ) {
set_source_code ( p_value ) ;
reload ( ) ;
return true ;
}
return false ;
}
void CSharpScript : : _get_property_list ( List < PropertyInfo > * p_properties ) const {
2021-11-03 23:06:17 +01:00
p_properties - > push_back ( PropertyInfo ( Variant : : STRING , CSharpLanguage : : singleton - > string_names . _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
}
2019-02-04 20:39:02 +01:00
Ref < CSharpScript > CSharpScript : : create_for_managed_type ( GDMonoClass * p_class , GDMonoClass * p_native ) {
2019-07-08 15:18:38 +02:00
// This method should not fail, only assertions allowed
2017-10-02 23:24:00 +02:00
2020-04-02 01:20:12 +02:00
CRASH_COND ( p_class = = nullptr ) ;
2017-10-02 23:24:00 +02:00
2019-07-03 09:44:53 +02:00
// TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
2017-10-02 23:24:00 +02:00
Ref < CSharpScript > script = memnew ( CSharpScript ) ;
2019-07-08 15:18:38 +02:00
initialize_for_managed_type ( script , p_class , p_native ) ;
return script ;
}
void CSharpScript : : initialize_for_managed_type ( Ref < CSharpScript > p_script , GDMonoClass * p_class , GDMonoClass * p_native ) {
// This method should not fail, only assertions allowed
2020-04-02 01:20:12 +02:00
CRASH_COND ( p_class = = nullptr ) ;
2019-07-08 15:18:38 +02:00
p_script - > name = p_class - > get_name ( ) ;
p_script - > script_class = p_class ;
p_script - > native = p_native ;
2017-10-02 23:24:00 +02:00
2020-04-02 01:20:12 +02:00
CRASH_COND ( p_script - > native = = nullptr ) ;
2017-10-02 23:24:00 +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 ) ;
# ifdef TOOLS_ENABLED
p_script - > _update_member_info_no_exports ( ) ;
# endif
}
// Extract information about the script using the mono class.
void CSharpScript : : update_script_class_info ( Ref < CSharpScript > p_script ) {
2019-07-08 15:18:38 +02:00
GDMonoClass * base = p_script - > script_class - > get_parent_class ( ) ;
2017-10-02 23:24:00 +02:00
2020-10-12 09:48:29 +02:00
// `base` should only be set if the script is a user defined type.
2020-07-05 19:19:36 +02:00
if ( base ! = p_script - > native ) {
2019-07-08 15:18:38 +02:00
p_script - > base = base ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
2019-07-08 15:18:38 +02:00
p_script - > tool = p_script - > script_class - > has_attribute ( CACHED_CLASS ( ToolAttribute ) ) ;
2019-07-03 09:44:53 +02:00
2019-07-08 15:18:38 +02:00
if ( ! p_script - > tool ) {
GDMonoClass * nesting_class = p_script - > script_class - > get_nesting_class ( ) ;
p_script - > tool = nesting_class & & nesting_class - > has_attribute ( CACHED_CLASS ( ToolAttribute ) ) ;
2019-07-03 09:44:53 +02:00
}
2020-11-29 09:12:06 +05:30
# ifdef TOOLS_ENABLED
2019-07-08 15:18:38 +02:00
if ( ! p_script - > tool ) {
p_script - > tool = p_script - > script_class - > get_assembly ( ) = = GDMono : : get_singleton ( ) - > get_tools_assembly ( ) ;
2019-07-03 09:44:53 +02:00
}
# endif
2017-10-02 23:24:00 +02:00
# ifdef DEBUG_ENABLED
// For debug builds, we must fetch from all native base methods as well.
// Native base methods must be fetched before the current class.
// Not needed if the script class itself is a native class.
2019-07-08 15:18:38 +02:00
if ( p_script - > script_class ! = p_script - > native ) {
GDMonoClass * native_top = p_script - > native ;
2017-10-02 23:24:00 +02:00
while ( native_top ) {
2019-07-08 15:18:38 +02:00
native_top - > fetch_methods_with_godot_api_checks ( p_script - > native ) ;
2017-10-02 23:24:00 +02:00
2020-07-05 19:19:36 +02:00
if ( native_top = = CACHED_CLASS ( GodotObject ) ) {
2017-10-02 23:24:00 +02:00
break ;
2020-07-05 19:19:36 +02:00
}
2017-10-02 23:24:00 +02:00
native_top = native_top - > get_parent_class ( ) ;
}
}
# endif
2019-07-08 15:18:38 +02:00
p_script - > script_class - > fetch_methods_with_godot_api_checks ( p_script - > native ) ;
2017-10-02 23:24:00 +02:00
2020-10-12 09:48:29 +02:00
p_script - > rpc_functions . clear ( ) ;
2019-07-08 15:18:38 +02:00
GDMonoClass * top = p_script - > script_class ;
while ( top & & top ! = p_script - > native ) {
2020-10-12 09:48:29 +02:00
// Fetch methods from base classes as well
2019-07-08 15:18:38 +02:00
top - > fetch_methods_with_godot_api_checks ( p_script - > native ) ;
2020-10-12 09:48:29 +02:00
// Update RPC info
{
Vector < GDMonoMethod * > methods = top - > get_all_methods ( ) ;
for ( int i = 0 ; i < methods . size ( ) ; i + + ) {
if ( ! methods [ i ] - > is_static ( ) ) {
2021-09-03 19:40:47 +02:00
Multiplayer : : RPCMode mode = p_script - > _member_get_rpc_mode ( methods [ i ] ) ;
if ( Multiplayer : : RPC_MODE_DISABLED ! = mode ) {
Multiplayer : : RPCConfig nd ;
2020-10-12 09:48:29 +02:00
nd . name = methods [ i ] - > get_name ( ) ;
2021-05-26 14:07:57 +02:00
nd . rpc_mode = mode ;
// TODO Transfer mode, channel
2021-09-03 19:40:47 +02:00
nd . transfer_mode = Multiplayer : : TRANSFER_MODE_RELIABLE ;
2021-05-26 14:07:57 +02:00
nd . channel = 0 ;
2020-10-12 09:48:29 +02:00
if ( - 1 = = p_script - > rpc_functions . find ( nd ) ) {
p_script - > rpc_functions . push_back ( nd ) ;
}
}
}
}
}
2017-10-02 23:24:00 +02:00
top = top - > get_parent_class ( ) ;
}
2020-10-12 09:48:29 +02:00
// Sort so we are 100% that they are always the same.
2021-09-03 19:40:47 +02:00
p_script - > rpc_functions . sort_custom < Multiplayer : : SortRPCConfig > ( ) ;
2020-10-12 09:48:29 +02:00
2019-07-08 15:18:38 +02:00
p_script - > load_script_signals ( p_script - > script_class , p_script - > native ) ;
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.
if ( extra_cond & & ! script_class ) {
2020-04-02 01:20:12 +02:00
if ( GDMono : : get_singleton ( ) - > get_project_assembly ( ) = = nullptr ) {
2018-10-22 19:43:19 +02:00
// The project assembly is not loaded
2020-04-02 01:20:12 +02:00
ERR_FAIL_V_MSG ( false , " Cannot instance script because the project assembly is not loaded. Script: ' " + get_path ( ) + " '. " ) ;
2018-10-22 19:43:19 +02:00
} else {
// The project assembly is loaded, but the class could not found
2020-04-02 01:20:12 +02:00
ERR_FAIL_V_MSG ( false , " Cannot instance script because the class ' " + name + " ' could not be found. Script: ' " + get_path ( ) + " '. " ) ;
2018-10-22 19:43:19 +02:00
}
}
return valid & & extra_cond ;
2017-10-02 23:24:00 +02:00
}
StringName CSharpScript : : get_instance_base_type ( ) const {
2020-07-05 19:19:36 +02:00
if ( native ) {
2017-10-02 23:24:00 +02:00
return native - > get_name ( ) ;
2020-07-05 19:19:36 +02:00
} else {
2017-10-02 23:24:00 +02:00
return StringName ( ) ;
2020-07-05 19:19:36 +02:00
}
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 ) {
2020-01-16 17:11:13 +01:00
GD_MONO_ASSERT_THREAD_ATTACHED ;
2017-10-02 23:24:00 +02:00
/* STEP 1, CREATE */
2019-02-28 23:12:49 +01:00
// Search the constructor first, to fail with an error if it's not found before allocating anything else.
GDMonoMethod * ctor = script_class - > get_method ( CACHED_STRING_NAME ( dotctor ) , p_argcount ) ;
2020-04-02 01:20:12 +02:00
if ( ctor = = nullptr ) {
ERR_FAIL_COND_V_MSG ( p_argcount = = 0 , nullptr ,
2019-08-09 03:39:45 +02:00
" Cannot create script instance. The class ' " + script_class - > get_full_name ( ) +
" ' does not define a parameterless constructor. " +
2020-12-15 12:04:21 +00:00
( get_path ( ) . is_empty ( ) ? String ( ) : " Path: ' " + get_path ( ) + " '. " ) ) ;
2019-02-28 23:12:49 +01:00
2020-04-02 01:20:12 +02:00
ERR_FAIL_V_MSG ( nullptr , " Constructor not found. " ) ;
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
CSharpScriptBinding & script_binding = ( ( Map < Object * , CSharpScriptBinding > : : Element * ) data ) - > get ( ) ;
2019-12-11 17:08:40 +01:00
if ( script_binding . inited & & ! script_binding . gchandle . is_released ( ) ) {
MonoObject * mono_object = script_binding . gchandle . get_target ( ) ;
2019-02-03 06:35:22 +01:00
if ( mono_object ) {
2020-04-02 01:20:12 +02:00
MonoException * exc = nullptr ;
2019-02-03 06:35:22 +01:00
GDMonoUtils : : dispose ( mono_object , & exc ) ;
if ( exc ) {
GDMonoUtils : : set_pending_exception ( exc ) ;
}
}
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 */
2019-07-03 09:44:53 +02:00
MonoObject * mono_object = mono_object_new ( mono_domain_get ( ) , script_class - > get_mono_ptr ( ) ) ;
2017-10-02 23:24:00 +02:00
if ( ! mono_object ) {
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 ;
2019-02-03 06:35:22 +01:00
bool die = instance - > _unreference_owner_unsafe ( ) ;
// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
2020-07-13 14:13:38 -04:00
CRASH_COND ( die ) ;
2019-02-03 06:35:22 +01:00
2020-04-02 01:20:12 +02:00
p_owner - > set_script_instance ( nullptr ) ;
2020-02-19 16:27:19 -03:00
r_error . error = Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_V_MSG ( nullptr , " Failed to allocate memory for the object. " ) ;
2017-10-02 23:24:00 +02:00
}
2018-11-30 20:43:06 +01:00
// Tie managed to unmanaged
2019-12-11 17:08:40 +01:00
instance - > gchandle = MonoGCHandleData : : new_strong_handle ( mono_object ) ;
2017-10-02 23:24:00 +02:00
2021-06-04 18:03:15 +02:00
if ( instance - > base_ref_counted ) {
2019-02-03 06:35:22 +01:00
instance - > _reference_owner_unsafe ( ) ; // Here, after assigning the gchandle (for the refcount_incremented callback)
2020-07-05 19:19:36 +02:00
}
2019-02-03 06:35:22 +01:00
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
instances . insert ( instance - > owner ) ;
}
2017-10-02 23:24:00 +02:00
CACHED_FIELD ( GodotObject , ptr ) - > set_value_raw ( mono_object , instance - > owner ) ;
// Construct
2018-06-26 21:03:42 +02:00
ctor - > invoke ( mono_object , p_args ) ;
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
ERR_FAIL_NULL_V ( native , Variant ( ) ) ;
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2021-06-17 16:03:09 -06:00
Object * owner = ClassDB : : instantiate ( NATIVE_GDMONOCLASS_NAME ( native ) ) ;
2017-10-02 23:24:00 +02:00
2020-01-16 17:11:13 +01:00
REF 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 ) {
ref = REF ( r ) ;
}
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
2017-10-02 23:24:00 +02:00
if ( native ) {
2020-03-14 19:20:17 +01:00
StringName native_name = NATIVE_GDMONOCLASS_NAME ( native ) ;
2017-10-02 23:24:00 +02:00
if ( ! ClassDB : : is_parent_class ( p_this - > get_class_name ( ) , native_name ) ) {
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) ) {
2020-03-14 19:20:17 +01:00
CSharpLanguage : : get_singleton ( ) - > debug_break_parse ( get_path ( ) , 0 ,
" Script inherits from native type ' " + String ( native_name ) +
2021-06-17 16:03:09 -06:00
" ', so it can't be instantiated in object of type: ' " + p_this - > get_class ( ) + " ' " ) ;
2017-10-02 23:24:00 +02:00
}
2021-10-28 15:19:35 +02:00
ERR_FAIL_V_MSG ( nullptr , " Script inherits from native type ' " + String ( native_name ) + " ', so it can't be instantiated in object of type: ' " + p_this - > get_class ( ) + " '. " ) ;
2017-10-02 23:24:00 +02:00
}
}
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
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 {
2020-07-05 19:19:36 +02:00
if ( ! script_class ) {
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
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2021-09-12 19:53:34 +02:00
// TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
GDMonoClass * top = script_class ;
while ( top & & top ! = native ) {
const Vector < GDMonoMethod * > & methods = top - > get_all_methods ( ) ;
for ( int i = 0 ; i < methods . size ( ) ; + + i ) {
MethodInfo minfo = methods [ i ] - > get_method_info ( ) ;
if ( minfo . name ! = CACHED_STRING_NAME ( dotctor ) ) {
p_list - > push_back ( methods [ i ] - > get_method_info ( ) ) ;
}
}
top = top - > get_parent_class ( ) ;
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 {
2020-07-05 19:19:36 +02:00
if ( ! script_class ) {
2018-07-25 21:30:11 +02:00
return false ;
2020-07-05 19:19:36 +02:00
}
2018-07-25 21:30:11 +02:00
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-01-09 17:19:03 +01:00
return script_class - > has_fetched_method_unknown_params ( p_method ) ;
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 {
2020-07-05 19:19:36 +02:00
if ( ! script_class ) {
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
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2018-11-01 08:35:16 -07:00
GDMonoClass * top = script_class ;
while ( top & & top ! = native ) {
GDMonoMethod * params = top - > get_fetched_method_unknown_params ( p_method ) ;
if ( params ) {
return params - > get_method_info ( ) ;
}
top = top - > get_parent_class ( ) ;
}
return MethodInfo ( ) ;
}
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
2020-01-16 17:11:13 +01:00
GD_MONO_SCOPE_THREAD_ATTACH ;
2021-03-06 00:12:42 +01:00
const DotNetScriptLookupInfo * lookup_info =
CSharpLanguage : : get_singleton ( ) - > lookup_dotnet_script ( get_path ( ) ) ;
if ( lookup_info ) {
GDMonoClass * klass = lookup_info - > script_class ;
if ( klass ) {
ERR_FAIL_COND_V ( ! CACHED_CLASS ( GodotObject ) - > is_assignable_from ( klass ) , FAILED ) ;
script_class = klass ;
2018-10-22 19:43:19 +02:00
}
2021-03-06 00:12:42 +01:00
}
2017-10-05 00:10:51 +02:00
2021-03-06 00:12:42 +01:00
valid = script_class ! = nullptr ;
2018-04-24 20:42:31 +02:00
2021-03-06 00:12:42 +01:00
if ( script_class ) {
2017-10-05 00:10:51 +02:00
# ifdef DEBUG_ENABLED
2021-03-06 00:12:42 +01:00
print_verbose ( " Found class " + script_class - > get_full_name ( ) + " for script " + get_path ( ) ) ;
2017-10-05 00:10:51 +02:00
# endif
2021-03-06 00:12:42 +01:00
native = GDMonoUtils : : get_class_native_base ( script_class ) ;
2017-10-02 23:24:00 +02:00
2021-03-06 00:12:42 +01:00
CRASH_COND ( native = = nullptr ) ;
2017-10-02 23:24:00 +02:00
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 ( ) ;
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
const Map < StringName , Variant > : : Element * E = exported_members_defval_cache . find ( p_property ) ;
if ( E ) {
r_value = E - > get ( ) ;
return true ;
}
if ( base_cache . is_valid ( ) ) {
return base_cache - > get_property_default_value ( p_property , r_value ) ;
}
# 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 {
2020-03-14 19:20:17 +01:00
return event_signals . has ( p_signal ) | | _signals . has ( p_signal ) ;
2018-01-18 23:27:43 +01:00
}
void CSharpScript : : get_script_signal_list ( List < MethodInfo > * r_signals ) const {
2021-07-26 21:31:17 +02:00
for ( const KeyValue < StringName , Vector < SignalParameter > > & E : _signals ) {
2018-01-18 23:27:43 +01:00
MethodInfo mi ;
2021-07-26 21:31:17 +02:00
mi . name = E . key ;
2020-03-14 19:20:17 +01:00
2021-07-26 21:31:17 +02:00
const Vector < SignalParameter > & params = E . value ;
2020-03-14 19:20:17 +01:00
for ( int i = 0 ; i < params . size ( ) ; i + + ) {
const SignalParameter & param = params [ i ] ;
PropertyInfo arg_info = PropertyInfo ( param . type , param . name ) ;
2020-07-05 19:19:36 +02:00
if ( param . type = = Variant : : NIL & & param . nil_is_variant ) {
2020-03-14 19:20:17 +01:00
arg_info . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
mi . arguments . push_back ( arg_info ) ;
}
r_signals - > push_back ( mi ) ;
}
2018-01-18 23:27:43 +01:00
2021-07-26 21:31:17 +02:00
for ( const KeyValue < StringName , EventSignal > & E : event_signals ) {
2020-03-14 19:20:17 +01:00
MethodInfo mi ;
2021-07-26 21:31:17 +02:00
mi . name = E . key ;
2020-03-14 19:20:17 +01:00
2021-07-26 21:31:17 +02:00
const EventSignal & event_signal = E . value ;
2020-03-14 19:20:17 +01:00
const Vector < SignalParameter > & params = event_signal . parameters ;
for ( int i = 0 ; i < params . size ( ) ; i + + ) {
const SignalParameter & param = params [ i ] ;
PropertyInfo arg_info = PropertyInfo ( param . type , param . name ) ;
2020-07-05 19:19:36 +02:00
if ( param . type = = Variant : : NIL & & param . nil_is_variant ) {
2020-03-14 19:20:17 +01:00
arg_info . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
2020-07-05 19:19:36 +02:00
}
2020-03-14 19:20:17 +01:00
mi . arguments . push_back ( arg_info ) ;
2018-01-18 23:27:43 +01:00
}
2020-03-14 19:20:17 +01:00
2018-01-18 23:27:43 +01:00
r_signals - > push_back ( mi ) ;
}
}
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 ;
}
2020-05-10 21:49:15 +02:00
if ( script_class = = nullptr | | cs - > script_class = = nullptr ) {
return false ;
}
if ( script_class = = cs - > script_class ) {
return true ;
}
return cs - > script_class - > is_assignable_from ( script_class ) ;
2020-04-20 19:06:00 -03:00
}
2017-10-02 23:24:00 +02:00
Ref < Script > CSharpScript : : get_base_script ( ) const {
// TODO search in metadata file once we have it, not important any way?
return Ref < Script > ( ) ;
}
2021-10-22 19:25:01 +02:00
void CSharpScript : : get_script_property_list ( List < PropertyInfo > * r_list ) const {
2021-10-30 16:55:21 +02:00
List < PropertyInfo > props ;
2021-10-22 19:25:01 +02:00
for ( OrderedHashMap < StringName , PropertyInfo > : : ConstElement E = member_info . front ( ) ; E ; E = E . next ( ) ) {
2021-10-30 16:55:21 +02:00
props . push_front ( E . value ( ) ) ;
}
for ( const PropertyInfo & prop : props ) {
r_list - > push_back ( prop ) ;
2017-10-02 23:24:00 +02:00
}
}
int CSharpScript : : get_member_line ( const StringName & p_member ) const {
// TODO omnisharp
return - 1 ;
}
2021-09-03 19:40:47 +02:00
Multiplayer : : RPCMode CSharpScript : : _member_get_rpc_mode ( IMonoClassMember * p_member ) const {
2021-10-01 10:43:22 +02:00
if ( p_member - > has_attribute ( CACHED_CLASS ( AnyPeerAttribute ) ) ) {
return Multiplayer : : RPC_MODE_ANY_PEER ;
2020-07-05 19:19:36 +02:00
}
2021-09-26 18:49:00 -04:00
if ( p_member - > has_attribute ( CACHED_CLASS ( AuthorityAttribute ) ) ) {
2021-09-03 19:40:47 +02:00
return Multiplayer : : RPC_MODE_AUTHORITY ;
2020-07-05 19:19:36 +02:00
}
2020-02-12 11:51:50 +01:00
2021-09-03 19:40:47 +02:00
return Multiplayer : : RPC_MODE_DISABLED ;
2020-02-12 11:51:50 +01:00
}
2021-09-03 19:40:47 +02:00
const Vector < Multiplayer : : RPCConfig > CSharpScript : : get_rpc_methods ( ) const {
2020-02-12 11:51:50 +01:00
return rpc_functions ;
}
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 : : _update_name ( ) {
String path = get_path ( ) ;
2020-12-15 12:04:21 +00:00
if ( ! path . is_empty ( ) ) {
2020-07-05 19:19:36 +02:00
name = get_path ( ) . get_file ( ) . get_basename ( ) ;
}
}
void CSharpScript : : _clear ( ) {
tool = false ;
valid = false ;
2021-12-06 19:45:59 +01:00
reload_invalidated = true ;
2020-07-05 19:19:36 +02:00
base = nullptr ;
native = nullptr ;
script_class = nullptr ;
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 ( ) ;
2020-07-05 19:19:36 +02:00
_update_name ( ) ;
2017-10-02 23:24:00 +02:00
# 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
}
2020-05-22 00:58:34 +02:00
void CSharpScript : : get_members ( Set < StringName > * p_members ) {
# 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 ***************/
2021-02-11 14:18:45 -03:00
RES 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?
CSharpScript * script = memnew ( CSharpScript ) ;
Ref < CSharpScript > scriptres ( script ) ;
# if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
Error err = script - > load_source_code ( p_path ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , RES ( ) , " Cannot load C# script file ' " + p_path + " '. " ) ;
2017-10-02 23:24:00 +02:00
# endif
script - > set_path ( p_original_path ) ;
2017-10-07 00:43:02 +02:00
2020-01-16 17:11:13 +01:00
script - > 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
return scriptres ;
}
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 ( ) : " " ;
}
Error ResourceFormatSaverCSharpScript : : save ( const String & p_path , const RES & p_resource , uint32_t p_flags ) {
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
Error err ;
FileAccess * file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " Cannot save C# script file ' " + p_path + " '. " ) ;
2017-10-02 23:24:00 +02:00
file - > store_string ( source ) ;
if ( file - > get_error ( ) ! = OK & & file - > get_error ( ) ! = ERR_FILE_EOF ) {
memdelete ( file ) ;
return ERR_CANT_CREATE ;
}
file - > close ( ) ;
memdelete ( file ) ;
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 ;
}
void ResourceFormatSaverCSharpScript : : get_recognized_extensions ( const RES & p_resource , List < String > * p_extensions ) const {
if ( Object : : cast_to < CSharpScript > ( p_resource . ptr ( ) ) ) {
p_extensions - > push_back ( " cs " ) ;
}
}
bool ResourceFormatSaverCSharpScript : : recognize ( const RES & 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
}
CSharpLanguage : : StringNameCache : : StringNameCache ( ) {
2017-10-16 03:54:23 +02:00
_signal_callback = StaticCString : : create ( " _signal_callback " ) ;
2017-10-02 23:24:00 +02:00
_set = StaticCString : : create ( " _set " ) ;
_get = StaticCString : : create ( " _get " ) ;
2019-03-28 11:01:43 +00:00
_get_property_list = StaticCString : : create ( " _get_property_list " ) ;
2017-10-02 23:24:00 +02:00
_notification = StaticCString : : create ( " _notification " ) ;
2017-10-30 22:17:20 +01:00
_script_source = StaticCString : : create ( " script/source " ) ;
2019-07-03 09:44:53 +02:00
on_before_serialize = StaticCString : : create ( " OnBeforeSerialize " ) ;
on_after_deserialize = StaticCString : : create ( " OnAfterDeserialize " ) ;
2017-10-02 23:24:00 +02:00
dotctor = StaticCString : : create ( " .ctor " ) ;
2020-03-14 19:20:17 +01:00
delegate_invoke_method_name = StaticCString : : create ( " Invoke " ) ;
2017-10-02 23:24:00 +02:00
}