summaryrefslogtreecommitdiff
path: root/modules/mono/csharp_script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/csharp_script.cpp')
-rw-r--r--modules/mono/csharp_script.cpp1073
1 files changed, 509 insertions, 564 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 25cc64393a..06793d25e0 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,28 +31,31 @@
#include "csharp_script.h"
#include <mono/metadata/threads.h>
+#include <mono/metadata/tokentype.h>
#include <stdint.h>
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
-#include "core/io/json.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
#include "core/os/thread.h"
#ifdef TOOLS_ENABLED
+#include "core/os/keyboard.h"
#include "editor/bindings_generator.h"
+#include "editor/editor_internal_calls.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/node_dock.h"
+#include "editor/script_templates/templates.gen.h"
#endif
#ifdef DEBUG_METHODS_ENABLED
#include "class_db_api_json.h"
#endif
-#include "editor/editor_internal_calls.h"
#include "godotsharp_dirs.h"
#include "mono_gd/gd_mono_cache.h"
#include "mono_gd/gd_mono_class.h"
@@ -82,6 +85,12 @@ static bool _create_project_solution_if_needed() {
CSharpLanguage *CSharpLanguage::singleton = nullptr;
+GDNativeInstanceBindingCallbacks CSharpLanguage::_instance_binding_callbacks = {
+ &_instance_binding_create_callback,
+ &_instance_binding_free_callback,
+ &_instance_binding_reference_callback
+};
+
String CSharpLanguage::get_name() const {
return "C#";
}
@@ -145,8 +154,8 @@ void CSharpLanguage::finalize() {
finalizing = true;
// Make sure all script binding gchandles are released before finalizing GDMono
- for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
- CSharpScriptBinding &script_binding = E->value();
+ for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
+ CSharpScriptBinding &script_binding = E.value;
if (!script_binding.gchandle.is_released()) {
script_binding.gchandle.release();
@@ -163,8 +172,8 @@ void CSharpLanguage::finalize() {
script_bindings.clear();
#ifdef DEBUG_ENABLED
- for (Map<ObjectID, int>::Element *E = unsafe_object_references.front(); E; E = E->next()) {
- const ObjectID &id = E->key();
+ for (const KeyValue<ObjectID, int> &E : unsafe_object_references) {
+ const ObjectID &id = E.key;
Object *obj = ObjectDB::get_instance(id);
if (obj) {
@@ -303,6 +312,26 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
}
}
+bool CSharpLanguage::is_control_flow_keyword(String p_keyword) const {
+ return p_keyword == "break" ||
+ 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";
+}
+
void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("//"); // single-line comment
p_delimiters->push_back("/* */"); // delimited comment
@@ -311,7 +340,7 @@ void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("' '"); // character literal
p_delimiters->push_back("\" \""); // regular string literal
- // Verbatim string literals (`@" "`) don't render correctly, so don't highlight them.
+ p_delimiters->push_back("@\" \""); // verbatim string literal
// Generic string highlighting suffices as a workaround for now.
}
@@ -323,57 +352,35 @@ static String get_base_class_name(const String &p_base_class_name, const String
return base_class;
}
-Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
- String script_template = "using " BINDINGS_NAMESPACE ";\n"
- "using System;\n"
- "\n"
- "public class %CLASS% : %BASE%\n"
- "{\n"
- " // Declare member variables here. Examples:\n"
- " // private int a = 2;\n"
- " // private string b = \"text\";\n"
- "\n"
- " // Called when the node enters the scene tree for the first time.\n"
- " public override void _Ready()\n"
- " {\n"
- " \n"
- " }\n"
- "\n"
- "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n"
- "// public override void _Process(float delta)\n"
- "// {\n"
- "// \n"
- "// }\n"
- "}\n";
-
- // Replaces all spaces in p_class_name with underscores to prevent
- // erronous C# Script templates from being generated when the object name
- // has spaces in it.
- 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);
- script_template = script_template.replace("%BASE%", base_class_name)
- .replace("%CLASS%", class_name_no_spaces);
-
- Ref<CSharpScript> script;
- script.instance();
- script->set_source_code(script_template);
- script->set_name(class_name_no_spaces);
-
- return script;
-}
-
bool CSharpLanguage::is_using_templates() {
return true;
}
-void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
- String src = p_script->get_source_code();
+Ref<Script> CSharpLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
+ Ref<CSharpScript> script;
+ script.instantiate();
+
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);
- src = src.replace("%BASE%", base_class_name)
- .replace("%CLASS%", class_name_no_spaces)
- .replace("%TS%", _get_indentation());
- p_script->set_source_code(src);
+ 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);
+ return script;
+}
+
+Vector<ScriptLanguage::ScriptTemplate> CSharpLanguage::get_built_in_templates(StringName p_object) {
+ Vector<ScriptLanguage::ScriptTemplate> templates;
+#ifdef TOOLS_ENABLED
+ for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) {
+ if (TEMPLATES[i].inherit == p_object) {
+ templates.append(TEMPLATES[i]);
+ }
+ }
+#endif
+ return templates;
}
String CSharpLanguage::validate_path(const String &p_path) const {
@@ -381,7 +388,7 @@ String CSharpLanguage::validate_path(const String &p_path) const {
List<String> keywords;
get_reserved_words(&keywords);
if (keywords.find(class_name)) {
- return TTR("Class name can't be a reserved keyword");
+ return RTR("Class name can't be a reserved keyword");
}
return "";
}
@@ -474,11 +481,14 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
Variant::VECTOR3,
Variant::VECTOR3I,
Variant::TRANSFORM2D,
+ Variant::VECTOR4,
+ Variant::VECTOR4I,
Variant::PLANE,
- Variant::QUAT,
+ Variant::QUATERNION,
Variant::AABB,
Variant::BASIS,
- Variant::TRANSFORM,
+ Variant::TRANSFORM3D,
+ Variant::PROJECTION,
Variant::COLOR,
Variant::STRING_NAME,
Variant::NODE_PATH,
@@ -522,10 +532,10 @@ String CSharpLanguage::make_function(const String &, const String &, const Packe
String CSharpLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0);
+ bool use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type");
if (use_space_indentation) {
- int indent_size = EDITOR_DEF("text_editor/indent/size", 4);
+ int indent_size = EDITOR_GET("text_editor/behavior/indent/size");
String space_indent = "";
for (int i = 0; i < indent_size; i++) {
@@ -679,10 +689,10 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) {
#ifdef DEBUG_ENABLED
MutexLock lock(unsafe_object_references_lock);
ObjectID id = p_obj->get_instance_id();
- Map<ObjectID, int>::Element *elem = unsafe_object_references.find(id);
+ HashMap<ObjectID, int>::Iterator elem = unsafe_object_references.find(id);
ERR_FAIL_NULL(elem);
- if (--elem->value() == 0) {
- unsafe_object_references.erase(elem);
+ if (--elem->value == 0) {
+ unsafe_object_references.remove(elem);
}
#endif
}
@@ -760,11 +770,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
- String appname = ProjectSettings::get_singleton()->get("application/config/name");
- String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.is_empty()) {
- appname_safe = "UnnamedProject";
- }
+ String appname_safe = ProjectSettings::get_singleton()->get_safe_project_name();
appname_safe += ".dll";
@@ -842,20 +848,25 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
List<Ref<CSharpScript>> to_reload;
// We need to keep reference instances alive during reloading
- List<Ref<Reference>> ref_instances;
+ List<Ref<RefCounted>> rc_instances;
- for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
- CSharpScriptBinding &script_binding = E->value();
- Reference *ref = Object::cast_to<Reference>(script_binding.owner);
- if (ref) {
- ref_instances.push_back(Ref<Reference>(ref));
+ for (const KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
+ const CSharpScriptBinding &script_binding = E.value;
+ RefCounted *rc = Object::cast_to<RefCounted>(script_binding.owner);
+ if (rc) {
+ rc_instances.push_back(Ref<RefCounted>(rc));
}
}
// As scripts are going to be reloaded, must proceed without locking here
- for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
- Ref<CSharpScript> &script = E->get();
+ for (Ref<CSharpScript> &script : scripts) {
+ // 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;
+ }
to_reload.push_back(script);
@@ -867,34 +878,31 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// 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.
- for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
- Object *obj = F->get();
+ for (Object *obj : script->instances) {
script->pending_reload_instances.insert(obj->get_instance_id());
- Reference *ref = Object::cast_to<Reference>(obj);
- if (ref) {
- ref_instances.push_back(Ref<Reference>(ref));
+ RefCounted *rc = Object::cast_to<RefCounted>(obj);
+ if (rc) {
+ rc_instances.push_back(Ref<RefCounted>(rc));
}
}
#ifdef TOOLS_ENABLED
- for (Set<PlaceHolderScriptInstance *>::Element *F = script->placeholders.front(); F; F = F->next()) {
- Object *obj = F->get()->get_owner();
+ for (PlaceHolderScriptInstance *script_instance : script->placeholders) {
+ Object *obj = script_instance->get_owner();
script->pending_reload_instances.insert(obj->get_instance_id());
- Reference *ref = Object::cast_to<Reference>(obj);
- if (ref) {
- ref_instances.push_back(Ref<Reference>(ref));
+ RefCounted *rc = Object::cast_to<RefCounted>(obj);
+ if (rc) {
+ rc_instances.push_back(Ref<RefCounted>(rc));
}
}
#endif
// Save state and remove script from instances
- Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
-
- for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
- Object *obj = F->get();
+ RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
+ for (Object *obj : script->instances) {
ERR_CONTINUE(!obj->get_script_instance());
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
@@ -916,12 +924,10 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
// After the state of all instances is saved, clear scripts and script instances
- for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
- Ref<CSharpScript> &script = E->get();
-
- while (script->instances.front()) {
- Object *obj = script->instances.front()->get();
- obj->set_script(REF()); // Remove script and existing script instances (placeholder are not removed before domain reload)
+ for (Ref<CSharpScript> &script : scripts) {
+ while (script->instances.begin()) {
+ Object *obj = *script->instances.begin();
+ obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload)
}
script->_clear();
@@ -931,11 +937,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
if (gdmono->reload_scripts_domain() != OK) {
// Failed to reload the scripts domain
// Make sure to add the scripts back to their owners before returning
- for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
- Ref<CSharpScript> scr = E->get();
-
- for (const Map<ObjectID, CSharpScript::StateBackup>::Element *F = scr->pending_reload_state.front(); F; F = F->next()) {
- Object *obj = ObjectDB::get_instance(F->key());
+ for (Ref<CSharpScript> &scr : to_reload) {
+ for (const KeyValue<ObjectID, CSharpScript::StateBackup> &F : scr->pending_reload_state) {
+ Object *obj = ObjectDB::get_instance(F.key);
if (!obj) {
continue;
@@ -955,8 +959,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
// Restore Variant properties state, it will be kept by the placeholder until the next script reloading
- for (List<Pair<StringName, Variant>>::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) {
- placeholder->property_set_fallback(G->get().first, G->get().second, nullptr);
+ for (const Pair<StringName, Variant> &G : scr->pending_reload_state[obj_id].properties) {
+ placeholder->property_set_fallback(G.first, G.second, nullptr);
}
scr->pending_reload_state.erase(obj_id);
@@ -968,9 +972,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
List<Ref<CSharpScript>> to_reload_state;
- for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
- Ref<CSharpScript> script = E->get();
-
+ for (Ref<CSharpScript> &script : to_reload) {
#ifdef TOOLS_ENABLED
script->exports_invalidated = true;
#endif
@@ -1023,8 +1025,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native);
{
- for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
- ObjectID obj_id = F->get();
+ for (const ObjectID &obj_id : script->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
if (!obj) {
@@ -1075,11 +1076,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
to_reload_state.push_back(script);
}
- for (List<Ref<CSharpScript>>::Element *E = to_reload_state.front(); E; E = E->next()) {
- Ref<CSharpScript> script = E->get();
-
- for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
- ObjectID obj_id = F->get();
+ for (Ref<CSharpScript> &script : to_reload_state) {
+ for (const ObjectID &obj_id : script->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
if (!obj) {
@@ -1093,25 +1091,25 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
- for (List<Pair<StringName, Variant>>::Element *G = state_backup.properties.front(); G; G = G->next()) {
- obj->get_script_instance()->set(G->get().first, G->get().second);
+ for (const Pair<StringName, Variant> &G : state_backup.properties) {
+ obj->get_script_instance()->set(G.first, G.second);
}
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
if (csi) {
- for (List<Pair<StringName, Array>>::Element *G = state_backup.event_signals.front(); G; G = G->next()) {
- const StringName &name = G->get().first;
- const Array &serialized_data = G->get().second;
+ for (const Pair<StringName, Array> &G : state_backup.event_signals) {
+ const StringName &name = G.first;
+ const Array &serialized_data = G.second;
- Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name);
+ HashMap<StringName, CSharpScript::EventSignal>::Iterator match = script->event_signals.find(name);
if (!match) {
// The event or its signal attribute were removed
continue;
}
- const CSharpScript::EventSignal &event_signal = match->value();
+ const CSharpScript::EventSignal &event_signal = match->value;
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
MonoDelegate *delegate = nullptr;
@@ -1146,9 +1144,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
{
MutexLock lock(ManagedCallable::instances_mutex);
- for (Map<ManagedCallable *, Array>::Element *elem = ManagedCallable::instances_pending_reload.front(); elem; elem = elem->next()) {
- ManagedCallable *managed_callable = elem->key();
- const Array &serialized_data = elem->value();
+ for (const KeyValue<ManagedCallable *, Array> &elem : ManagedCallable::instances_pending_reload) {
+ ManagedCallable *managed_callable = elem.key;
+ const Array &serialized_data = elem.value;
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
MonoDelegate *delegate = nullptr;
@@ -1175,53 +1173,63 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#ifdef TOOLS_ENABLED
// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
if (Engine::get_singleton()->is_editor_hint()) {
- EditorNode::get_singleton()->get_inspector()->update_tree();
- NodeDock::singleton->update_lists();
+ InspectorDock::get_inspector_singleton()->update_tree();
+ NodeDock::get_singleton()->update_lists();
}
#endif
}
#endif
-void CSharpLanguage::_load_scripts_metadata() {
- scripts_metadata.clear();
+void CSharpLanguage::lookup_script_for_class(GDMonoClass *p_class) {
+ if (!p_class->has_attribute(CACHED_CLASS(ScriptPathAttribute))) {
+ return;
+ }
- String scripts_metadata_filename = "scripts_metadata.";
+ MonoObject *attr = p_class->get_attribute(CACHED_CLASS(ScriptPathAttribute));
+ String path = CACHED_FIELD(ScriptPathAttribute, path)->get_string_value(attr);
-#ifdef TOOLS_ENABLED
- scripts_metadata_filename += Engine::get_singleton()->is_editor_hint() ? "editor" : "editor_player";
-#else
-#ifdef DEBUG_ENABLED
- scripts_metadata_filename += "debug";
-#else
- scripts_metadata_filename += "release";
-#endif
-#endif
+ dotnet_script_lookup_map[path] = DotNetScriptLookupInfo(
+ p_class->get_namespace(), p_class->get_name(), p_class);
+}
- String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file(scripts_metadata_filename);
+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);
- if (FileAccess::exists(scripts_metadata_path)) {
- String old_json;
+ 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();
- Error ferr = read_all_file_utf8(scripts_metadata_path, old_json);
+ int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
- ERR_FAIL_COND(ferr != OK);
+ 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);
- Variant old_dict_var;
- String err_str;
- int err_line;
- Error json_err = JSON::parse(old_json, old_dict_var, err_str, err_line);
- if (json_err != OK) {
- ERR_PRINT("Failed to parse metadata file: '" + err_str + "' (" + String::num_int64(err_line) + ").");
- return;
- }
+ if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) {
+ continue;
+ }
+
+ 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);
- scripts_metadata = old_dict_var.operator Dictionary();
- scripts_metadata_invalidated = false;
+ int length = mono_array_length(script_types);
- print_verbose("Successfully loaded scripts metadata");
- } else {
- if (!Engine::get_singleton()->is_editor_hint()) {
- ERR_PRINT("Missing scripts metadata file.");
+ 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);
+ }
}
}
}
@@ -1282,8 +1290,8 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
}
void CSharpLanguage::_on_scripts_domain_unloaded() {
- for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
- CSharpScriptBinding &script_binding = E->value();
+ for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
+ CSharpScriptBinding &script_binding = E.value;
script_binding.gchandle.release();
script_binding.inited = false;
}
@@ -1300,7 +1308,7 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
}
#endif
- scripts_metadata_invalidated = true;
+ dotnet_script_lookup_map.clear();
}
#ifdef TOOLS_ENABLED
@@ -1325,6 +1333,7 @@ void CSharpLanguage::_editor_init_callback() {
// Enable it as a plugin
EditorNode::add_editor_plugin(godotsharp_editor);
+ ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B);
godotsharp_editor->enable_plugin();
get_singleton()->godotsharp_editor = godotsharp_editor;
@@ -1407,61 +1416,61 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
r_script_binding.owner = p_object;
// Tie managed to unmanaged
- Reference *ref = Object::cast_to<Reference>(p_object);
+ RefCounted *rc = Object::cast_to<RefCounted>(p_object);
- if (ref) {
+ if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
- // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
+ // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
- ref->reference();
- CSharpLanguage::get_singleton()->post_unsafe_reference(ref);
+ rc->reference();
+ CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
return true;
}
-void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
- MutexLock lock(language_bind_mutex);
+RBMap<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();
- Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object);
+ MutexLock lock(csharp_lang->language_bind_mutex);
+
+ RBMap<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance);
if (match) {
return (void *)match;
}
CSharpScriptBinding script_binding;
- if (!setup_csharp_script_binding(script_binding, p_object)) {
- return nullptr;
- }
-
- return (void *)insert_script_binding(p_object, script_binding);
+ return (void *)csharp_lang->insert_script_binding((Object *)p_instance, script_binding);
}
-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_free_callback(void *, void *, void *p_binding) {
+ CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton();
-void CSharpLanguage::free_instance_binding_data(void *p_data) {
if (GDMono::get_singleton() == nullptr) {
#ifdef DEBUG_ENABLED
- CRASH_COND(!script_bindings.is_empty());
+ CRASH_COND(!csharp_lang->script_bindings.is_empty());
#endif
// Mono runtime finalized, all the gchandle bindings were already released
return;
}
- if (finalizing) {
+ if (csharp_lang->finalizing) {
return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there
}
GD_MONO_ASSERT_THREAD_ATTACHED;
{
- MutexLock lock(language_bind_mutex);
+ MutexLock lock(csharp_lang->language_bind_mutex);
- Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data;
+ RBMap<Object *, CSharpScriptBinding>::Element *data = (RBMap<Object *, CSharpScriptBinding>::Element *)p_binding;
CSharpScriptBinding &script_binding = data->value();
@@ -1475,99 +1484,122 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
script_binding.gchandle.release();
}
- script_bindings.erase(data);
+ csharp_lang->script_bindings.erase(data);
}
}
-void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
- Reference *ref_owner = Object::cast_to<Reference>(p_object);
+GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) {
+ CRASH_COND(!p_binding);
+
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
+
+ RefCounted *rc_owner = Object::cast_to<RefCounted>(script_binding.owner);
#ifdef DEBUG_ENABLED
- CRASH_COND(!ref_owner);
- CRASH_COND(!p_object->has_script_instance_binding(get_language_index()));
+ CRASH_COND(!rc_owner);
#endif
- void *data = p_object->get_script_instance_binding(get_language_index());
- CRASH_COND(!data);
-
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
MonoGCHandleData &gchandle = script_binding.gchandle;
+ int refcount = rc_owner->reference_get_count();
+
if (!script_binding.inited) {
- return;
+ return refcount == 0;
}
- if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
+ 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;
- // 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.
+ // 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; // Called after the managed side was collected, so nothing to do here
- }
+ MonoObject *target = gchandle.get_target();
+ if (!target) {
+ return false; // Called after the managed side was collected, so nothing to do here
+ }
- // Release the current weak handle and replace it with a strong handle.
- MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target);
- gchandle.release();
- gchandle = strong_gchandle;
- }
-}
+ // 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;
+ }
-bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
- Reference *ref_owner = Object::cast_to<Reference>(p_object);
+ 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;
-#ifdef DEBUG_ENABLED
- CRASH_COND(!ref_owner);
- CRASH_COND(!p_object->has_script_instance_binding(get_language_index()));
-#endif
+ // If owner owner is no longer referenced by the unmanaged side,
+ // the managed instance takes responsibility of deleting the owner when GCed.
- void *data = p_object->get_script_instance_binding(get_language_index());
- CRASH_COND(!data);
+ MonoObject *target = gchandle.get_target();
+ if (!target) {
+ return refcount == 0; // Called after the managed side was collected, so nothing to do here
+ }
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
- MonoGCHandleData &gchandle = script_binding.gchandle;
+ // 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;
- int refcount = ref_owner->reference_get_count();
+ return false;
+ }
- if (!script_binding.inited) {
return refcount == 0;
}
+}
- if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
+void *CSharpLanguage::get_instance_binding(Object *p_object) {
+ void *binding = p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
- // If owner owner is no longer referenced by the unmanaged side,
- // the managed instance takes responsibility of deleting the owner when GCed.
+ // 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.
- MonoObject *target = gchandle.get_target();
- if (!target) {
- return refcount == 0; // Called after the managed side was collected, so nothing to do here
- }
+ if (binding) {
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)binding)->value();
- // 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;
+ if (!script_binding.inited) {
+ MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
- return false;
+ if (!script_binding.inited) { // Another thread may have set it up
+ CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, p_object);
+ }
+ }
}
- return refcount == 0;
+ return binding;
+}
+
+void *CSharpLanguage::get_existing_instance_binding(Object *p_object) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_object->has_instance_binding(p_object));
+#endif
+ 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());
}
CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
- Reference *ref = Object::cast_to<Reference>(p_owner);
+ RefCounted *rc = Object::cast_to<RefCounted>(p_owner);
- instance->base_ref = ref != nullptr;
+ instance->base_ref_counted = rc != nullptr;
instance->owner = p_owner;
instance->gchandle = p_gchandle;
- if (instance->base_ref) {
+ if (instance->base_ref_counted) {
instance->_reference_owner_unsafe();
}
@@ -1606,7 +1638,7 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
GDMonoProperty *property = top->get_property(p_name);
if (property) {
- property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type()));
+ property->set_value_from_variant(mono_object, p_value);
return true;
}
@@ -1703,16 +1735,25 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
}
void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state) {
- List<PropertyInfo> pinfo;
- get_property_list(&pinfo);
+ List<PropertyInfo> property_list;
+ get_property_list(&property_list);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ for (const PropertyInfo &prop_info : property_list) {
Pair<StringName, Variant> state_pair;
- state_pair.first = E->get().name;
+ state_pair.first = prop_info.name;
ManagedType managedType;
- GDMonoField *field = script->script_class->get_field(state_pair.first);
+ 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();
+ }
if (!field) {
continue; // Properties ignored. We get the property baking fields instead.
}
@@ -1731,8 +1772,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
MonoObject *owner_managed = get_mono_object();
ERR_FAIL_NULL(owner_managed);
- for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
- const CSharpScript::EventSignal &event_signal = E->value();
+ for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
+ const CSharpScript::EventSignal &event_signal = E.value;
MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
if (!delegate_field_value) {
@@ -1759,8 +1800,9 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
}
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
- p_properties->push_back(E->value());
+ List<PropertyInfo> props;
+ for (const KeyValue<StringName, PropertyInfo> &E : script->member_info) {
+ props.push_front(E.value);
}
// Call _get_property_list
@@ -1783,9 +1825,8 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
if (ret) {
Array array = Array(GDMonoMarshal::mono_object_to_variant(ret));
for (int i = 0, size = array.size(); i < size; i++) {
- p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
+ props.push_back(PropertyInfo::from_dict(array.get(i)));
}
- return;
}
break;
@@ -1793,6 +1834,10 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
top = top->get_parent_class();
}
+
+ for (const PropertyInfo &prop : props) {
+ p_properties->push_back(prop);
+ }
}
Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
@@ -1810,6 +1855,29 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *
return Variant::NIL;
}
+void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
+ if (!script->is_valid() || !script->script_class) {
+ return;
+ }
+
+ 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();
+ }
+}
+
bool CSharpInstance::has_method(const StringName &p_method) const {
if (!script.is_valid()) {
return false;
@@ -1830,7 +1898,7 @@ bool CSharpInstance::has_method(const StringName &p_method) const {
return false;
}
-Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
ERR_FAIL_COND_V(!script.is_valid(), Variant());
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -1869,7 +1937,7 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args,
bool CSharpInstance::_reference_owner_unsafe() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
CRASH_COND(unsafe_referenced); // already referenced
#endif
@@ -1880,7 +1948,7 @@ bool CSharpInstance::_reference_owner_unsafe() {
// See: _unreference_owner_unsafe()
// May not me referenced yet, so we must use init_ref() instead of reference()
- if (static_cast<Reference *>(owner)->init_ref()) {
+ if (static_cast<RefCounted *>(owner)->init_ref()) {
CSharpLanguage::get_singleton()->post_unsafe_reference(owner);
unsafe_referenced = true;
}
@@ -1890,7 +1958,7 @@ bool CSharpInstance::_reference_owner_unsafe() {
bool CSharpInstance::_unreference_owner_unsafe() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
#endif
@@ -1907,7 +1975,7 @@ bool CSharpInstance::_unreference_owner_unsafe() {
// Destroying the owner here means self destructing, so we defer the owner destruction to the caller.
CSharpLanguage::get_singleton()->pre_unsafe_unreference(owner);
- return static_cast<Reference *>(owner)->unreference();
+ return static_cast<RefCounted *>(owner)->unreference();
}
MonoObject *CSharpInstance::_internal_new_managed() {
@@ -1939,7 +2007,7 @@ MonoObject *CSharpInstance::_internal_new_managed() {
// Tie managed to unmanaged
gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (base_ref) {
+ if (base_ref_counted) {
_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
}
@@ -1956,7 +2024,7 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
disconnect_event_signals();
#ifdef DEBUG_ENABLED
- CRASH_COND(base_ref);
+ CRASH_COND(base_ref_counted);
CRASH_COND(gchandle.is_released());
#endif
CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle);
@@ -1964,7 +2032,7 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(gchandle.is_released());
#endif
@@ -1999,41 +2067,38 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
}
void CSharpInstance::connect_event_signals() {
- for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
- const CSharpScript::EventSignal &event_signal = E->value();
+ for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
+ const CSharpScript::EventSignal &event_signal = E.value;
StringName signal_name = event_signal.field->get_name();
// TODO: Use pooling for ManagedCallable instances.
- auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
+ EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
- owner->connect(signal_name, Callable(event_signal_callable));
+ Callable callable(event_signal_callable);
+ connected_event_signals.push_back(callable);
+ owner->connect(signal_name, callable);
}
}
void CSharpInstance::disconnect_event_signals() {
- for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
- const CSharpScript::EventSignal &event_signal = E->value();
-
- StringName signal_name = event_signal.field->get_name();
-
- // TODO: It would be great if we could store this EventSignalCallable on the stack.
- // The problem is that Callable memdeletes it when it's destructed...
- auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
-
- owner->disconnect(signal_name, Callable(event_signal_callable));
+ for (const Callable &callable : connected_event_signals) {
+ const EventSignalCallable *event_signal_callable = static_cast<const EventSignalCallable *>(callable.get_custom());
+ owner->disconnect(event_signal_callable->get_signal(), callable);
}
+
+ connected_event_signals.clear();
}
void CSharpInstance::refcount_incremented() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
#endif
- Reference *ref_owner = Object::cast_to<Reference>(owner);
+ RefCounted *rc_owner = Object::cast_to<RefCounted>(owner);
- if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
// The reference count was increased after the managed side was the only one referencing our owner.
@@ -2049,13 +2114,13 @@ void CSharpInstance::refcount_incremented() {
bool CSharpInstance::refcount_decremented() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
#endif
- Reference *ref_owner = Object::cast_to<Reference>(owner);
+ RefCounted *rc_owner = Object::cast_to<RefCounted>(owner);
- int refcount = ref_owner->reference_get_count();
+ int refcount = rc_owner->reference_get_count();
if (refcount == 1 && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -2076,44 +2141,8 @@ bool CSharpInstance::refcount_decremented() {
return ref_dying;
}
-Vector<ScriptNetData> CSharpInstance::get_rpc_methods() const {
- return script->get_rpc_methods();
-}
-
-uint16_t CSharpInstance::get_rpc_method_id(const StringName &p_method) const {
- return script->get_rpc_method_id(p_method);
-}
-
-StringName CSharpInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_method(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_mode_by_id(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const {
- return script->get_rpc_mode(p_method);
-}
-
-Vector<ScriptNetData> CSharpInstance::get_rset_properties() const {
- return script->get_rset_properties();
-}
-
-uint16_t CSharpInstance::get_rset_property_id(const StringName &p_variable) const {
- return script->get_rset_property_id(p_variable);
-}
-
-StringName CSharpInstance::get_rset_property(const uint16_t p_rset_member_id) const {
- return script->get_rset_property(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- return script->get_rset_mode_by_id(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const {
- return script->get_rset_mode(p_variable);
+const Variant CSharpInstance::get_rpc_config() const {
+ return script->get_rpc_config();
}
void CSharpInstance::notification(int p_notification) {
@@ -2126,12 +2155,12 @@ void CSharpInstance::notification(int p_notification) {
predelete_notified = true;
- if (base_ref) {
- // It's not safe to proceed if the owner derives Reference and the refcount reached 0.
+ if (base_ref_counted) {
+ // It's not safe to proceed if the owner derives RefCounted and the refcount reached 0.
// 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.
- // However, this means C# Reference scripts can't receive NOTIFICATION_PREDELETE, but
+ // However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but
// this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784
return;
}
@@ -2257,15 +2286,15 @@ CSharpInstance::~CSharpInstance() {
}
// If not being called from the owner's destructor, and we still hold a reference to the owner
- if (base_ref && !ref_dying && owner && unsafe_referenced) {
+ if (base_ref_counted && !ref_dying && owner && unsafe_referenced) {
// The owner's script or script instance is being replaced (or removed)
// Transfer ownership to an "instance binding"
- Reference *ref_owner = static_cast<Reference *>(owner);
+ RefCounted *rc_owner = static_cast<RefCounted *>(owner);
// We will unreference the owner before referencing it again, so we need to keep it alive
- Ref<Reference> scope_keep_owner_alive(ref_owner);
+ Ref<RefCounted> scope_keep_owner_alive(rc_owner);
(void)scope_keep_owner_alive;
// Unreference the owner here, before the new "instance binding" references it.
@@ -2273,24 +2302,14 @@ CSharpInstance::~CSharpInstance() {
bool die = _unreference_owner_unsafe();
CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die
- void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ void *data = CSharpLanguage::get_instance_binding(owner);
CRASH_COND(data == nullptr);
-
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
-
- if (!script_binding.inited) {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
-
- if (!script_binding.inited) { // Other thread may have set it up
- // Already had a binding that needs to be setup
- CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, owner);
- CRASH_COND(!script_binding.inited);
- }
- }
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CRASH_COND(!script_binding.inited);
#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
- CRASH_COND(ref_owner->reference_get_count() <= 1);
+ CRASH_COND(rc_owner->reference_get_count() <= 1);
#endif
}
@@ -2299,9 +2318,9 @@ CSharpInstance::~CSharpInstance() {
#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);
+ HashSet<Object *>::Iterator match = script->instances.find(owner);
CRASH_COND(!match);
- script->instances.erase(match);
+ script->instances.remove(match);
#else
script->instances.erase(owner);
#endif
@@ -2315,17 +2334,17 @@ void CSharpScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
#endif
#ifdef TOOLS_ENABLED
-void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) {
+void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) {
if (base_cache.is_valid()) {
base_cache->_update_exports_values(values, propnames);
}
- for (Map<StringName, Variant>::Element *E = exported_members_defval_cache.front(); E; E = E->next()) {
- values[E->key()] = E->get();
+ for (const KeyValue<StringName, Variant> &E : exported_members_defval_cache) {
+ values[E.key] = E.value;
}
- for (List<PropertyInfo>::Element *E = exported_members_cache.front(); E; E = E->next()) {
- propnames.push_back(E->get());
+ for (const PropertyInfo &prop_info : exported_members_cache) {
+ propnames.push_back(prop_info);
}
}
@@ -2377,7 +2396,7 @@ void CSharpScript::_update_member_info_no_exports() {
}
#endif
-bool CSharpScript::_update_exports() {
+bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_update) {
#ifdef TOOLS_ENABLED
bool is_editor = Engine::get_singleton()->is_editor_hint();
if (is_editor) {
@@ -2518,7 +2537,7 @@ bool CSharpScript::_update_exports() {
#ifdef TOOLS_ENABLED
if (is_editor) {
// Need to check this here, before disposal
- bool base_ref = Object::cast_to<Reference>(tmp_native) != nullptr;
+ bool base_ref_counted = Object::cast_to<RefCounted>(tmp_native) != nullptr;
// Dispose the temporary managed instance
@@ -2533,7 +2552,7 @@ bool CSharpScript::_update_exports() {
GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
tmp_object = nullptr;
- if (tmp_native && !base_ref) {
+ if (tmp_native && !base_ref_counted) {
Node *node = Object::cast_to<Node>(tmp_native);
if (node && node->is_inside_tree()) {
ERR_PRINT("Temporary instance was added to the scene tree.");
@@ -2549,14 +2568,18 @@ bool CSharpScript::_update_exports() {
if (is_editor) {
placeholder_fallback_enabled = false;
- if (placeholders.size()) {
+ if ((changed || p_instance_to_update) && placeholders.size()) {
// Update placeholders if any
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ if (changed) {
+ for (PlaceHolderScriptInstance *script_instance : placeholders) {
+ script_instance->update(propnames, values);
+ }
+ } else {
+ p_instance_to_update->update(propnames, values);
}
}
}
@@ -2787,7 +2810,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
GD_MONO_ASSERT_THREAD_ATTACHED;
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;
+ MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
+ r_hint = GDMonoUtils::Marshal::type_has_flags_attribute(reftype) ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
@@ -2824,7 +2848,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
ERR_FAIL_COND_V_MSG(r_error, -1, "Failed to unbox '" + enum_field_name + "' constant enum value.");
- if (val != (unsigned int)i) {
+ unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i;
+ if (val != expected_val) {
uses_default_values = false;
}
@@ -2844,6 +2869,12 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
r_hint = PROPERTY_HINT_RESOURCE_TYPE;
r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
+ } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(Node)->is_assignable_from(p_type.type_class)) {
+ GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
+ CRASH_COND(field_native_class == nullptr);
+
+ r_hint = PROPERTY_HINT_NODE_TYPE;
+ r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
// Nested arrays are not supported in the inspector
@@ -2860,12 +2891,24 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
ERR_FAIL_COND_V_MSG(elem_variant_type == Variant::NIL, -1, "Unknown array element type.");
- int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
+ 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;
+ }
+ }
+
+ 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);
- ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
+ ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
+
+ // Format: type/hint:hint_string
+ r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
+ }
- // Format: type/hint:hint_string
- r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
r_hint = PROPERTY_HINT_TYPE_STRING;
} else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) {
@@ -2878,7 +2921,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
}
#endif
-Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Variant CSharpScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (unlikely(GDMono::get_singleton() == nullptr)) {
// Probably not the best error but eh.
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
@@ -2906,7 +2949,7 @@ Variant CSharpScript::call(const StringName &p_method, const Variant **p_args, i
}
// No static method found. Try regular instance calls
- return Script::call(p_method, p_args, p_argcount, r_error);
+ return Script::callp(p_method, p_args, p_argcount, r_error);
}
void CSharpScript::_resource_path_changed() {
@@ -2933,7 +2976,7 @@ bool CSharpScript::_set(const StringName &p_name, const Variant &p_value) {
}
void CSharpScript::_get_property_list(List<PropertyInfo> *p_properties) const {
- p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
void CSharpScript::_bind_methods() {
@@ -2965,6 +3008,7 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
CRASH_COND(p_script->native == nullptr);
p_script->valid = true;
+ p_script->reload_invalidated = false;
update_script_class_info(p_script);
@@ -3016,8 +3060,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native);
- p_script->rpc_functions.clear();
- p_script->rpc_variables.clear();
+ p_script->rpc_config.clear();
GDMonoClass *top = p_script->script_class;
while (top && top != p_script->native) {
@@ -3029,48 +3072,9 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
Vector<GDMonoMethod *> methods = top->get_all_methods();
for (int i = 0; i < methods.size(); i++) {
if (!methods[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = methods[i]->get_name();
- nd.mode = mode;
- if (-1 == p_script->rpc_functions.find(nd)) {
- p_script->rpc_functions.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoField *> fields = top->get_all_fields();
- for (int i = 0; i < fields.size(); i++) {
- if (!fields[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(fields[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = fields[i]->get_name();
- nd.mode = mode;
- if (-1 == p_script->rpc_variables.find(nd)) {
- p_script->rpc_variables.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoProperty *> properties = top->get_all_properties();
- for (int i = 0; i < properties.size(); i++) {
- if (!properties[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(properties[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = properties[i]->get_name();
- nd.mode = mode;
- if (-1 == p_script->rpc_variables.find(nd)) {
- p_script->rpc_variables.push_back(nd);
- }
+ const Variant rpc_config = p_script->_member_get_rpc_config(methods[i]);
+ if (rpc_config.get_type() != Variant::NIL) {
+ p_script->rpc_config[methods[i]->get_name()] = rpc_config;
}
}
}
@@ -3079,14 +3083,10 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
top = top->get_parent_class();
}
- // Sort so we are 100% that they are always the same.
- p_script->rpc_functions.sort_custom<SortNetData>();
- p_script->rpc_variables.sort_custom<SortNetData>();
-
p_script->load_script_signals(p_script->script_class, p_script->native);
}
-bool CSharpScript::can_instance() const {
+bool CSharpScript::can_instantiate() const {
#ifdef TOOLS_ENABLED
bool extra_cond = tool || ScriptServer::is_scripting_enabled();
#else
@@ -3117,7 +3117,7 @@ StringName CSharpScript::get_instance_base_type() const {
}
}
-CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) {
+CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
GD_MONO_ASSERT_THREAD_ATTACHED;
/* STEP 1, CREATE */
@@ -3133,18 +3133,18 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
ERR_FAIL_V_MSG(nullptr, "Constructor not found.");
}
- Ref<Reference> ref;
- if (p_isref) {
+ Ref<RefCounted> ref;
+ if (p_is_ref_counted) {
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
- ref = Ref<Reference>(static_cast<Reference *>(p_owner));
+ ref = Ref<RefCounted>(static_cast<RefCounted *>(p_owner));
}
// If the object had a script instance binding, dispose it before adding the CSharpInstance
- if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) {
- void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ if (CSharpLanguage::has_instance_binding(p_owner)) {
+ void *data = CSharpLanguage::get_existing_instance_binding(p_owner);
CRASH_COND(data == nullptr);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited && !script_binding.gchandle.is_released()) {
MonoObject *mono_object = script_binding.gchandle.get_target();
if (mono_object) {
@@ -3162,7 +3162,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
}
CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this)));
- instance->base_ref = p_isref;
+ instance->base_ref_counted = p_is_ref_counted;
instance->owner = p_owner;
instance->owner->set_script_instance(instance);
@@ -3187,7 +3187,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Tie managed to unmanaged
instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (instance->base_ref) {
+ if (instance->base_ref_counted) {
instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
}
@@ -3219,18 +3219,18 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
GD_MONO_SCOPE_THREAD_ATTACH;
- Object *owner = ClassDB::instance(NATIVE_GDMONOCLASS_NAME(native));
+ Object *owner = ClassDB::instantiate(NATIVE_GDMONOCLASS_NAME(native));
- REF ref;
- Reference *r = Object::cast_to<Reference>(owner);
+ Ref<RefCounted> ref;
+ RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
- ref = REF(r);
+ ref = Ref<RefCounted>(r);
}
CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error);
if (!instance) {
if (ref.is_null()) {
- memdelete(owner); //no owner, sorry
+ memdelete(owner); // no owner, sorry
}
return Variant();
}
@@ -3247,30 +3247,29 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
CRASH_COND(!valid);
#endif
+ GD_MONO_SCOPE_THREAD_ATTACH;
+
if (native) {
StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
if (EngineDebugger::is_active()) {
CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
"Script inherits from native type '" + String(native_name) +
- "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
+ "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
}
- ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) +
- "', so it can't be instanced in object of type: '" + p_this->get_class() + "'.");
+ 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() + "'.");
}
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
Callable::CallError unchecked_error;
- return _create_instance(nullptr, 0, p_this, Object::cast_to<Reference>(p_this) != nullptr, unchecked_error);
+ return _create_instance(nullptr, 0, p_this, Object::cast_to<RefCounted>(p_this) != nullptr, unchecked_error);
}
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);
- _update_exports();
+ _update_exports(si);
return si;
#else
return nullptr;
@@ -3307,10 +3306,19 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
GD_MONO_SCOPE_THREAD_ATTACH;
- // TODO: Filter out things unsuitable for explicit calls, like constructors.
- const Vector<GDMonoMethod *> &methods = script_class->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- p_list->push_back(methods[i]->get_method_info());
+ // 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();
}
}
@@ -3346,55 +3354,44 @@ MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
}
Error CSharpScript::reload(bool p_keep_state) {
- bool has_instances;
- {
- MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
- has_instances = instances.size();
+ if (!reload_invalidated) {
+ return OK;
}
- ERR_FAIL_COND_V(!p_keep_state && has_instances, ERR_ALREADY_IN_USE);
+ // In the case of C#, reload doesn't really do any script reloading.
+ // That's done separately via domain reloading.
+ reload_invalidated = false;
GD_MONO_SCOPE_THREAD_ATTACH;
- GDMonoAssembly *project_assembly = GDMono::get_singleton()->get_project_assembly();
+ const DotNetScriptLookupInfo *lookup_info =
+ CSharpLanguage::get_singleton()->lookup_dotnet_script(get_path());
- if (project_assembly) {
- const Variant *script_metadata_var = CSharpLanguage::get_singleton()->get_scripts_metadata().getptr(get_path());
- if (script_metadata_var) {
- Dictionary script_metadata = script_metadata_var->operator Dictionary()["class"];
- const Variant *namespace_ = script_metadata.getptr("namespace");
- const Variant *class_name = script_metadata.getptr("class_name");
- ERR_FAIL_NULL_V(namespace_, ERR_BUG);
- ERR_FAIL_NULL_V(class_name, ERR_BUG);
- GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String());
- if (klass && CACHED_CLASS(GodotObject)->is_assignable_from(klass)) {
- script_class = klass;
- }
- } else {
- // Missing script metadata. Fallback to legacy method
- script_class = project_assembly->get_object_derived_class(name);
+ 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;
}
+ }
- valid = script_class != nullptr;
+ valid = script_class != nullptr;
- if (script_class) {
+ if (script_class) {
#ifdef DEBUG_ENABLED
- print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
+ print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
#endif
- native = GDMonoUtils::get_class_native_base(script_class);
+ native = GDMonoUtils::get_class_native_base(script_class);
- CRASH_COND(native == nullptr);
+ CRASH_COND(native == nullptr);
- update_script_class_info(this);
+ update_script_class_info(this);
- _update_exports();
- }
-
- return OK;
+ _update_exports();
}
- return ERR_FILE_MISSING_DEPENDENCIES;
+ return OK;
}
ScriptLanguage *CSharpScript::get_language() const {
@@ -3404,9 +3401,9 @@ ScriptLanguage *CSharpScript::get_language() const {
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);
+ HashMap<StringName, Variant>::ConstIterator E = exported_members_defval_cache.find(p_property);
if (E) {
- r_value = E->get();
+ r_value = E->value;
return true;
}
@@ -3429,11 +3426,11 @@ bool CSharpScript::has_script_signal(const StringName &p_signal) const {
}
void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const Map<StringName, Vector<SignalParameter>>::Element *E = _signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Vector<SignalParameter>> &E : _signals) {
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
- const Vector<SignalParameter> &params = E->value();
+ const Vector<SignalParameter> &params = E.value;
for (int i = 0; i < params.size(); i++) {
const SignalParameter &param = params[i];
@@ -3448,11 +3445,11 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
r_signals->push_back(mi);
}
- for (const Map<StringName, EventSignal>::Element *E = event_signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, EventSignal> &E : event_signals) {
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
- const EventSignal &event_signal = E->value();
+ const EventSignal &event_signal = E.value;
const Vector<SignalParameter> &params = event_signal.parameters;
for (int i = 0; i < params.size(); i++) {
const SignalParameter &param = params[i];
@@ -3491,102 +3488,51 @@ Ref<Script> CSharpScript::get_base_script() const {
return Ref<Script>();
}
-void CSharpScript::get_script_property_list(List<PropertyInfo> *p_list) const {
- for (Map<StringName, PropertyInfo>::Element *E = member_info.front(); E; E = E->next()) {
- p_list->push_back(E->value());
- }
-}
+void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
+ List<PropertyInfo> props;
-int CSharpScript::get_member_line(const StringName &p_member) const {
- // TODO omnisharp
- return -1;
-}
-
-MultiplayerAPI::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
- if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) {
- return MultiplayerAPI::RPC_MODE_REMOTE;
- }
- if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) {
- return MultiplayerAPI::RPC_MODE_MASTER;
- }
- if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) {
- return MultiplayerAPI::RPC_MODE_PUPPET;
- }
- if (p_member->has_attribute(CACHED_CLASS(RemoteSyncAttribute))) {
- return MultiplayerAPI::RPC_MODE_REMOTESYNC;
- }
- if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute))) {
- return MultiplayerAPI::RPC_MODE_MASTERSYNC;
+ for (const KeyValue<StringName, PropertyInfo> &E : member_info) {
+ props.push_front(E.value);
}
- if (p_member->has_attribute(CACHED_CLASS(PuppetSyncAttribute))) {
- return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
- }
-
- return MultiplayerAPI::RPC_MODE_DISABLED;
-}
-Vector<ScriptNetData> CSharpScript::get_rpc_methods() const {
- return rpc_functions;
-}
-
-uint16_t CSharpScript::get_rpc_method_id(const StringName &p_method) const {
- for (int i = 0; i < rpc_functions.size(); i++) {
- if (rpc_functions[i].name == p_method) {
- return i;
- }
+ for (const PropertyInfo &prop : props) {
+ r_list->push_back(prop);
}
- return UINT16_MAX;
}
-StringName CSharpScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
- ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), StringName());
- return rpc_functions[p_rpc_method_id].name;
-}
-
-MultiplayerAPI::RPCMode CSharpScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
- return rpc_functions[p_rpc_method_id].mode;
-}
-
-MultiplayerAPI::RPCMode CSharpScript::get_rpc_mode(const StringName &p_method) const {
- return get_rpc_mode_by_id(get_rpc_method_id(p_method));
+int CSharpScript::get_member_line(const StringName &p_member) const {
+ // TODO omnisharp
+ return -1;
}
-Vector<ScriptNetData> CSharpScript::get_rset_properties() const {
- return rpc_variables;
-}
+Variant CSharpScript::_member_get_rpc_config(IMonoClassMember *p_member) const {
+ Variant out;
-uint16_t CSharpScript::get_rset_property_id(const StringName &p_variable) const {
- for (int i = 0; i < rpc_variables.size(); i++) {
- if (rpc_variables[i].name == p_variable) {
- return i;
- }
+ MonoObject *rpc_attribute = p_member->get_attribute(CACHED_CLASS(RPCAttribute));
+ if (rpc_attribute != nullptr) {
+ Dictionary rpc_config;
+ rpc_config["rpc_mode"] = CACHED_PROPERTY(RPCAttribute, Mode)->get_int_value(rpc_attribute);
+ rpc_config["call_local"] = CACHED_PROPERTY(RPCAttribute, CallLocal)->get_bool_value(rpc_attribute);
+ rpc_config["transfer_mode"] = CACHED_PROPERTY(RPCAttribute, TransferMode)->get_int_value(rpc_attribute);
+ rpc_config["channel"] = CACHED_PROPERTY(RPCAttribute, TransferChannel)->get_int_value(rpc_attribute);
+ out = rpc_config;
}
- return UINT16_MAX;
-}
-
-StringName CSharpScript::get_rset_property(const uint16_t p_rset_member_id) const {
- ERR_FAIL_COND_V(p_rset_member_id >= rpc_variables.size(), StringName());
- return rpc_variables[p_rset_member_id].name;
-}
-MultiplayerAPI::RPCMode CSharpScript::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- ERR_FAIL_COND_V(p_rset_member_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
- return rpc_functions[p_rset_member_id].mode;
+ return out;
}
-MultiplayerAPI::RPCMode CSharpScript::get_rset_mode(const StringName &p_variable) const {
- return get_rset_mode_by_id(get_rset_property_id(p_variable));
+const Variant CSharpScript::get_rpc_config() const {
+ return rpc_config;
}
Error CSharpScript::load_source_code(const String &p_path) {
Error ferr = read_all_file_utf8(p_path, source);
ERR_FAIL_COND_V_MSG(ferr != OK, ferr,
- 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 + "'.");
+ 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 + "'.");
#ifdef TOOLS_ENABLED
source_changed_cache = true;
@@ -3606,6 +3552,7 @@ void CSharpScript::_update_name() {
void CSharpScript::_clear() {
tool = false;
valid = false;
+ reload_invalidated = true;
base = nullptr;
native = nullptr;
@@ -3632,11 +3579,11 @@ CSharpScript::~CSharpScript() {
#endif
}
-void CSharpScript::get_members(Set<StringName> *p_members) {
+void CSharpScript::get_members(HashSet<StringName> *p_members) {
#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
if (p_members) {
- for (Set<StringName>::Element *E = exported_members_names.front(); E; E = E->next()) {
- p_members->insert(E->get());
+ for (const StringName &member_name : exported_members_names) {
+ p_members->insert(member_name);
}
}
#endif
@@ -3644,7 +3591,7 @@ void CSharpScript::get_members(Set<StringName> *p_members) {
/*************** RESOURCE ***************/
-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) {
+Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -3657,7 +3604,7 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
Error err = script->load_source_code(p_path);
- ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load C# script file '" + p_path + "'.");
+ ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot load C# script file '" + p_path + "'.");
#endif
script->set_path(p_original_path);
@@ -3683,7 +3630,7 @@ String ResourceFormatLoaderCSharpScript::get_resource_type(const String &p_path)
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) {
+Error ResourceFormatSaverCSharpScript::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
Ref<CSharpScript> sqscr = p_resource;
ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
@@ -3699,20 +3646,18 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
}
#endif
- Error err;
- FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save C# script file '" + p_path + "'.");
+ {
+ Error err;
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save C# script file '" + p_path + "'.");
- file->store_string(source);
+ file->store_string(source);
- if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
- memdelete(file);
- return ERR_CANT_CREATE;
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ return ERR_CANT_CREATE;
+ }
}
- file->close();
- memdelete(file);
-
#ifdef TOOLS_ENABLED
if (ScriptServer::is_reload_scripts_on_save_enabled()) {
CSharpLanguage::get_singleton()->reload_tool_script(p_resource, false);
@@ -3722,13 +3667,13 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
return OK;
}
-void ResourceFormatSaverCSharpScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
+void ResourceFormatSaverCSharpScript::get_recognized_extensions(const Ref<Resource> &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 {
+bool ResourceFormatSaverCSharpScript::recognize(const Ref<Resource> &p_resource) const {
return Object::cast_to<CSharpScript>(p_resource.ptr()) != nullptr;
}