summaryrefslogtreecommitdiff
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/csharp_script.cpp72
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/mono/editor/bindings_generator.cpp58
-rw-r--r--modules/mono/editor/bindings_generator.h3
-rw-r--r--modules/mono/editor/godotsharp_editor.cpp7
-rw-r--r--modules/mono/glue/cs_files/Color.cs26
-rw-r--r--modules/mono/glue/cs_files/DebuggingUtils.cs77
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp14
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp73
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h24
12 files changed, 298 insertions, 68 deletions
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 949c636050..db2d0a9780 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -445,6 +445,72 @@ String CSharpLanguage::_get_indentation() const {
return "\t";
}
+Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
+
+ // Printing an error here will result in endless recursion, so we must be careful
+
+ if (!gdmono->is_runtime_initialized() && GDMono::get_singleton()->get_api_assembly())
+ return Vector<StackInfo>();
+
+ 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;
+}
+
+Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
+
+ // Printing an error here could result in endless recursion, so we must be careful
+
+ MonoObject *exc = NULL;
+
+ GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames);
+ MonoArray *frames = st_get_frames(p_stack_trace, &exc);
+
+ if (exc) {
+ GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ return Vector<StackInfo>();
+ }
+
+ int frame_count = mono_array_length(frames);
+
+ if (frame_count <= 0)
+ return Vector<StackInfo>();
+
+ GDMonoUtils::DebugUtils_StackFrameInfo get_sf_info = CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo);
+
+ Vector<StackInfo> si;
+ si.resize(frame_count);
+
+ for (int i = 0; i < frame_count; i++) {
+ StackInfo &sif = si[i];
+ MonoObject *frame = mono_array_get(frames, MonoObject *, i);
+
+ MonoString *file_name;
+ int file_line_num;
+ MonoString *method_decl;
+ get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc);
+
+ if (exc) {
+ GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
+ return Vector<StackInfo>();
+ }
+
+ 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;
+}
+
void CSharpLanguage::frame() {
const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle;
@@ -1049,7 +1115,7 @@ bool CSharpInstance::has_method(const StringName &p_method) const {
GDMonoClass *top = script->script_class;
while (top && top != script->native) {
- if (top->has_method(p_method)) {
+ if (top->has_fetched_method_unknown_params(p_method)) {
return true;
}
@@ -1227,7 +1293,7 @@ ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method)
GDMonoClass *top = script->script_class;
while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(p_method);
+ GDMonoMethod *method = top->get_fetched_method_unknown_params(p_method);
if (method && !method->is_static())
return _member_get_rpc_mode(method);
@@ -1848,7 +1914,7 @@ void CSharpScript::set_source_code(const String &p_code) {
bool CSharpScript::has_method(const StringName &p_method) const {
- return script_class->has_method(p_method);
+ return script_class->has_fetched_method_unknown_params(p_method);
}
Error CSharpScript::reload(bool p_keep_state) {
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 171601f3d8..3ce8a9b64e 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -303,7 +303,7 @@ public:
/* TODO */ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { return ""; }
- /* TODO */ virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
+ virtual Vector<StackInfo> debug_get_current_stack_info();
/* PROFILING FUNCTIONS */
/* TODO */ virtual void profiling_start() {}
@@ -335,6 +335,8 @@ public:
virtual void *alloc_instance_binding_data(Object *p_object);
virtual void free_instance_binding_data(void *p_data);
+ Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
+
CSharpLanguage();
~CSharpLanguage();
};
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 2205ac4e98..800e3b723b 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -448,14 +448,14 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_output_dir, bo
compile_items.push_back(output_file);
}
- for (Map<StringName, TypeInterface>::Element *E = obj_types.front(); E; E = E->next()) {
- const TypeInterface &itype = E->get();
+ for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
+ const TypeInterface &itype = E.get();
if (itype.api_type == ClassDB::API_EDITOR)
continue;
- String output_file = path_join(obj_type_dir, E->get().proxy_name + ".cs");
- Error err = _generate_cs_type(E->get(), output_file);
+ String output_file = path_join(obj_type_dir, itype.proxy_name + ".cs");
+ Error err = _generate_cs_type(itype, output_file);
if (err == ERR_SKIP)
continue;
@@ -580,14 +580,14 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_output_dir,
if (!solution.set_path(p_output_dir))
return ERR_FILE_NOT_FOUND;
- for (Map<StringName, TypeInterface>::Element *E = obj_types.front(); E; E = E->next()) {
- const TypeInterface &itype = E->get();
+ for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
+ const TypeInterface &itype = E.get();
if (itype.api_type != ClassDB::API_EDITOR)
continue;
- String output_file = path_join(obj_type_dir, E->get().proxy_name + ".cs");
- Error err = _generate_cs_type(E->get(), output_file);
+ String output_file = path_join(obj_type_dir, itype.proxy_name + ".cs");
+ Error err = _generate_cs_type(itype, output_file);
if (err == ERR_SKIP)
continue;
@@ -945,7 +945,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
return ERR_BUG;
}
- Map<StringName, TypeInterface>::Element *object_itype = obj_types.find("Object");
+ OrderedHashMap<StringName, TypeInterface>::Element object_itype = obj_types.find("Object");
if (!object_itype) {
ERR_PRINT("BUG: Object type interface not found!");
@@ -953,7 +953,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.push_back(MEMBER_BEGIN "public " CS_CLASS_SIGNALAWAITER " ToSignal(");
- output.push_back(object_itype->get().cs_type);
+ output.push_back(object_itype.get().cs_type);
output.push_back(" source, string signal)\n" OPEN_BLOCK_L2
"return new " CS_CLASS_SIGNALAWAITER "(source, signal, this);\n" CLOSE_BLOCK_L2);
}
@@ -999,9 +999,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Search it in base types too
const TypeInterface *current_type = &p_itype;
while (!setter && current_type->base_name != StringName()) {
- Map<StringName, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_NULL_V(base_match, ERR_BUG);
- current_type = &base_match->get();
+ OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
+ ERR_FAIL_COND_V(!base_match, ERR_BUG);
+ current_type = &base_match.get();
setter = current_type->find_method_by_name(p_iprop.setter);
}
@@ -1010,9 +1010,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Search it in base types too
current_type = &p_itype;
while (!getter && current_type->base_name != StringName()) {
- Map<StringName, TypeInterface>::Element *base_match = obj_types.find(current_type->base_name);
- ERR_FAIL_NULL_V(base_match, ERR_BUG);
- current_type = &base_match->get();
+ OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
+ ERR_FAIL_COND_V(!base_match, ERR_BUG);
+ current_type = &base_match.get();
getter = current_type->find_method_by_name(p_iprop.getter);
}
@@ -1324,8 +1324,8 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
generated_icall_funcs.clear();
- for (Map<StringName, TypeInterface>::Element *type_elem = obj_types.front(); type_elem; type_elem = type_elem->next()) {
- const TypeInterface &itype = type_elem->get();
+ for (OrderedHashMap<StringName, TypeInterface>::Element type_elem = obj_types.front(); type_elem; type_elem = type_elem.next()) {
+ const TypeInterface &itype = type_elem.get();
List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
@@ -1631,20 +1631,20 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const StringName &p_cname) {
- const Map<StringName, TypeInterface>::Element *match = builtin_types.find(p_cname);
+ const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_cname);
- if (match)
- return &match->get();
+ if (builtin_type_match)
+ return &builtin_type_match->get();
- match = obj_types.find(p_cname);
+ const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_cname);
- if (match)
- return &match->get();
+ if (obj_type_match)
+ return &obj_type_match.get();
- match = enum_types.find(p_cname);
+ const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_cname);
- if (match)
- return &match->get();
+ if (enum_match)
+ return &enum_match->get();
return NULL;
}
@@ -2484,8 +2484,8 @@ void BindingsGenerator::initialize() {
_generate_header_icalls();
- for (Map<StringName, TypeInterface>::Element *E = obj_types.front(); E; E = E->next())
- _generate_method_icalls(E->get());
+ for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next())
+ _generate_method_icalls(E.get());
_generate_method_icalls(builtin_types["NodePath"]);
_generate_method_icalls(builtin_types["RID"]);
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 717a6b7a6b..8929b45cce 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -429,10 +429,11 @@ class BindingsGenerator {
static bool verbose_output;
+ OrderedHashMap<StringName, TypeInterface> obj_types;
+
Map<StringName, TypeInterface> placeholder_types;
Map<StringName, TypeInterface> builtin_types;
Map<StringName, TypeInterface> enum_types;
- Map<StringName, TypeInterface> obj_types;
List<EnumInterface> global_enums;
List<ConstantInterface> global_constants;
diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp
index da0a7b4fbd..9e48da68c1 100644
--- a/modules/mono/editor/godotsharp_editor.cpp
+++ b/modules/mono/editor/godotsharp_editor.cpp
@@ -151,7 +151,7 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
if (p_line >= 0) {
args.push_back("-g");
- args.push_back(script_path + ":" + itos(p_line) + ":" + itos(p_col));
+ args.push_back(script_path + ":" + itos(p_line + 1) + ":" + itos(p_col));
} else {
args.push_back(script_path);
}
@@ -170,6 +170,11 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
monodevel_instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path()));
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
+
+ if (p_line >= 0) {
+ script_path += ";" + itos(p_line + 1) + ";" + itos(p_col);
+ }
+
monodevel_instance->execute(script_path);
} break;
default:
diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index db0e1fb744..f9e31e9703 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -336,7 +336,7 @@ namespace Godot
this.r = (rgba & 0xFF) / 255.0f;
}
- private static float _parse_col(string str, int ofs)
+ private static int _parse_col(string str, int ofs)
{
int ig = 0;
@@ -415,17 +415,17 @@ namespace Godot
if (alpha)
{
- if ((int)_parse_col(color, 0) < 0)
+ if (_parse_col(color, 0) < 0)
return false;
}
int from = alpha ? 2 : 0;
- if ((int)_parse_col(color, from + 0) < 0)
+ if (_parse_col(color, from + 0) < 0)
return false;
- if ((int)_parse_col(color, from + 2) < 0)
+ if (_parse_col(color, from + 2) < 0)
return false;
- if ((int)_parse_col(color, from + 4) < 0)
+ if (_parse_col(color, from + 4) < 0)
return false;
return true;
@@ -467,10 +467,10 @@ namespace Godot
if (alpha)
{
- a = _parse_col(rgba, 0);
+ a = _parse_col(rgba, 0) / 255f;
if (a < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Alpha is " + a + " but zero or greater is expected: " + rgba);
+ throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
}
else
{
@@ -479,20 +479,20 @@ namespace Godot
int from = alpha ? 2 : 0;
- r = _parse_col(rgba, from + 0);
+ r = _parse_col(rgba, from + 0) / 255f;
if (r < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Red is " + r + " but zero or greater is expected: " + rgba);
+ throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
- g = _parse_col(rgba, from + 2);
+ g = _parse_col(rgba, from + 2) / 255f;
if (g < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Green is " + g + " but zero or greater is expected: " + rgba);
+ throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
- b = _parse_col(rgba, from + 4);
+ b = _parse_col(rgba, from + 4) / 255f;
if (b < 0)
- throw new ArgumentOutOfRangeException("Invalid color code. Blue is " + b + " but zero or greater is expected: " + rgba);
+ throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
}
public static bool operator ==(Color left, Color right)
diff --git a/modules/mono/glue/cs_files/DebuggingUtils.cs b/modules/mono/glue/cs_files/DebuggingUtils.cs
new file mode 100644
index 0000000000..ced78f658d
--- /dev/null
+++ b/modules/mono/glue/cs_files/DebuggingUtils.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Text;
+
+namespace Godot
+{
+ internal static class DebuggingUtils
+ {
+ internal static void AppendTypeName(this StringBuilder sb, Type type)
+ {
+ if (type.IsPrimitive)
+ sb.Append(type.Name);
+ else if (type == typeof(void))
+ sb.Append("void");
+ else
+ sb.Append(type.ToString());
+
+ sb.Append(" ");
+ }
+
+ public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl)
+ {
+ fileName = frame.GetFileName();
+ fileLineNumber = frame.GetFileLineNumber();
+
+ MethodBase methodBase = frame.GetMethod();
+
+ StringBuilder sb = new StringBuilder();
+
+ if (methodBase is MethodInfo methodInfo)
+ sb.AppendTypeName(methodInfo.ReturnType);
+
+ sb.Append(methodBase.DeclaringType.FullName);
+ sb.Append(".");
+ sb.Append(methodBase.Name);
+
+ if (methodBase.IsGenericMethod)
+ {
+ Type[] genericParams = methodBase.GetGenericArguments();
+
+ sb.Append("<");
+
+ for (int j = 0; j < genericParams.Length; j++)
+ {
+ if (j > 0)
+ sb.Append(", ");
+
+ sb.AppendTypeName(genericParams[j]);
+ }
+
+ sb.Append(">");
+ }
+
+ sb.Append("(");
+
+ bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0;
+
+ ParameterInfo[] parameter = methodBase.GetParameters();
+
+ for (int i = 0; i < parameter.Length; i++)
+ {
+ if (i > 0)
+ sb.Append(", ");
+
+ if (i == parameter.Length - 1 && varArgs)
+ sb.Append("params ");
+
+ sb.AppendTypeName(parameter[i].ParameterType);
+ }
+
+ sb.Append(")");
+
+ methodDecl = sb.ToString();
+ }
+ }
+}
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index d4df7e0cb2..6c07c90f79 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -226,7 +226,7 @@ void GDMono::initialize() {
mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
- OS::get_singleton()->print("Mono: ALL IS GOOD\n");
+ OS::get_singleton()->print("Mono: INITIALIZED\n");
}
#ifndef MONO_GLUE_DISABLED
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index d3315568cb..b826352f02 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -89,11 +89,6 @@ Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
}
#endif
-bool GDMonoClass::has_method(const StringName &p_name) {
-
- return get_method(p_name) != NULL;
-}
-
bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
@@ -225,7 +220,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
methods_fetched = true;
}
-GDMonoMethod *GDMonoClass::get_method(const StringName &p_name) {
+GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
ERR_FAIL_COND_V(!methods_fetched, NULL);
@@ -239,6 +234,11 @@ GDMonoMethod *GDMonoClass::get_method(const StringName &p_name) {
return NULL;
}
+bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
+
+ return get_fetched_method_unknown_params(p_name) != NULL;
+}
+
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count);
@@ -303,6 +303,8 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
mono_method_desc_free(desc);
+ ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, NULL);
+
return get_method(method);
}
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index b6052ac0ed..f5895be144 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -112,7 +112,8 @@ public:
Vector<MonoClassField *> get_enum_fields();
#endif
- bool has_method(const StringName &p_name);
+ GDMonoMethod *get_fetched_method_unknown_params(const StringName &p_name);
+ bool has_fetched_method_unknown_params(const StringName &p_name);
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
@@ -120,8 +121,7 @@ public:
void fetch_attributes();
void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
- GDMonoMethod *get_method(const StringName &p_name);
- GDMonoMethod *get_method(const StringName &p_name, int p_params_count);
+ GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index ff999b36f2..d02c73978e 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -31,6 +31,7 @@
#include "gd_mono_utils.h"
#include "os/dir_access.h"
+#include "os/os.h"
#include "project_settings.h"
#include "reference.h"
@@ -53,6 +54,7 @@ MonoCache mono_cache;
#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_ns##_##m_class, m_val)
#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.rawclass_##m_class, m_val)
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val)
+#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val)
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
void MonoCache::clear_members() {
@@ -72,6 +74,13 @@ void MonoCache::clear_members() {
class_String = NULL;
class_IntPtr = NULL;
+#ifdef DEBUG_ENABLED
+ class_System_Diagnostics_StackTrace = NULL;
+ methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL;
+ method_System_Diagnostics_StackTrace_ctor_bool = NULL;
+ method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
+#endif
+
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
@@ -94,6 +103,11 @@ void MonoCache::clear_members() {
class_WeakRef = NULL;
class_MarshalUtils = NULL;
+#ifdef DEBUG_ENABLED
+ class_DebuggingUtils = NULL;
+ methodthunk_DebuggingUtils_GetStackFrameInfo = NULL;
+#endif
+
class_ExportAttribute = NULL;
field_ExportAttribute_hint = NULL;
field_ExportAttribute_hintString = NULL;
@@ -137,6 +151,13 @@ void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
+
+#ifdef DEBUG_ENABLED
+ CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
+ CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames")->get_thunk());
+ CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
+ CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
+#endif
}
void update_godot_api_cache() {
@@ -161,6 +182,10 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
+#ifdef DEBUG_ENABLED
+ CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
+#endif
+
// Attributes
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
@@ -183,6 +208,10 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
+#ifdef DEBUG_ENABLED
+ CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk());
+#endif
+
{
/*
* TODO Right now we only support Dictionary<object, object>.
@@ -366,9 +395,47 @@ String get_exception_name_and_message(MonoObject *p_ex) {
return res;
}
-void print_unhandled_exception(MonoObject *p_ex) {
- ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_ex).utf8());
- mono_print_unhandled_exception(p_ex);
+void print_unhandled_exception(MonoObject *p_exc) {
+ print_unhandled_exception(p_exc, false);
+}
+
+void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
+ mono_print_unhandled_exception(p_exc);
+#ifdef DEBUG_ENABLED
+ GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
+ MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
+
+ MonoBoolean need_file_info = true;
+ void *ctor_args[2] = { p_exc, &need_file_info };
+
+ MonoObject *unexpected_exc = NULL;
+ CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
+
+ if (unexpected_exc != NULL) {
+ mono_print_unhandled_exception(unexpected_exc);
+
+ if (p_recursion_caution) {
+ // Called from CSharpLanguage::get_current_stack_info,
+ // so printing an error here could result in endless recursion
+ OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
+ return;
+ } else {
+ ERR_FAIL();
+ }
+ }
+
+ Vector<ScriptLanguage::StackInfo> si;
+ if (stack_trace != NULL && !p_recursion_caution)
+ si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
+
+ String file = si.size() ? si[0].file : __FILE__;
+ String func = si.size() ? si[0].func : FUNCTION_STR;
+ int line = si.size() ? si[0].line : __LINE__;
+ String error_msg = "Unhandled exception";
+ String exc_msg = GDMonoUtils::get_exception_name_and_message(p_exc);
+
+ ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
+#endif
}
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 284585856e..597397eced 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -46,13 +46,10 @@ typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoO
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray **, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
+typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **);
+typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
struct MonoCache {
- // Format for cached classes in the Godot namespace: class_<Class>
- // Macro: CACHED_CLASS(<Class>)
-
- // Format for cached classes in a different namespace: class_<Namespace>_<Class>
- // Macro: CACHED_NS_CLASS(<Namespace>, <Class>)
// -----------------------------------------------
// corlib classes
@@ -73,6 +70,13 @@ struct MonoCache {
GDMonoClass *class_String;
GDMonoClass *class_IntPtr;
+#ifdef DEBUG_ENABLED
+ GDMonoClass *class_System_Diagnostics_StackTrace;
+ StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames;
+ GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool;
+ GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
+#endif
+
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
@@ -96,6 +100,11 @@ struct MonoCache {
GDMonoClass *class_WeakRef;
GDMonoClass *class_MarshalUtils;
+#ifdef DEBUG_ENABLED
+ GDMonoClass *class_DebuggingUtils;
+ DebugUtils_StackFrameInfo methodthunk_DebuggingUtils_GetStackFrameInfo;
+#endif
+
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hintString;
@@ -167,7 +176,8 @@ MonoDomain *create_domain(const String &p_friendly_name);
String get_exception_name_and_message(MonoObject *p_ex);
-void print_unhandled_exception(MonoObject *p_ex);
+void print_unhandled_exception(MonoObject *p_exc);
+void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
} // namespace GDMonoUtils
@@ -175,9 +185,9 @@ void print_unhandled_exception(MonoObject *p_ex);
#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class)
#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_mono_ptr())
-#define CACHED_NS_CLASS(m_ns, m_class) (GDMonoUtils::mono_cache.class_##m_ns##_##m_class)
#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class)
#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)
+#define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method)
#ifdef REAL_T_IS_DOUBLE