Merge pull request #15463 from neikeq/the-stack-frame-madness
Mono: Implement stack info for errors and exceptions
This commit is contained in:
commit
c037f6339f
17 changed files with 330 additions and 90 deletions
|
@ -294,6 +294,11 @@ void ScriptDebuggerLocal::send_message(const String &p_message, const Array &p_a
|
|||
print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));
|
||||
}
|
||||
|
||||
void ScriptDebuggerLocal::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
|
||||
|
||||
print_line("ERROR: '" + (p_descr.empty() ? p_err : p_descr) + "'");
|
||||
}
|
||||
|
||||
ScriptDebuggerLocal::ScriptDebuggerLocal() {
|
||||
|
||||
profiling = false;
|
||||
|
|
|
@ -44,6 +44,7 @@ class ScriptDebuggerLocal : public ScriptDebugger {
|
|||
public:
|
||||
void debug(ScriptLanguage *p_script, bool p_can_continue);
|
||||
virtual void send_message(const String &p_message, const Array &p_args);
|
||||
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
|
||||
|
||||
virtual bool is_profiling() const { return profiling; }
|
||||
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) {}
|
||||
|
|
|
@ -432,22 +432,6 @@ void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char
|
|||
if (p_type == ERR_HANDLER_SCRIPT)
|
||||
return; //ignore script errors, those go through debugger
|
||||
|
||||
ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
|
||||
|
||||
OutputError oe;
|
||||
oe.error = p_err;
|
||||
oe.error_descr = p_descr;
|
||||
oe.source_file = p_file;
|
||||
oe.source_line = p_line;
|
||||
oe.source_func = p_func;
|
||||
oe.warning = p_type == ERR_HANDLER_WARNING;
|
||||
uint64_t time = OS::get_singleton()->get_ticks_msec();
|
||||
oe.hr = time / 3600000;
|
||||
oe.min = (time / 60000) % 60;
|
||||
oe.sec = (time / 1000) % 60;
|
||||
oe.msec = time % 1000;
|
||||
Array cstack;
|
||||
|
||||
Vector<ScriptLanguage::StackInfo> si;
|
||||
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
|
@ -456,32 +440,8 @@ void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char
|
|||
break;
|
||||
}
|
||||
|
||||
cstack.resize(si.size() * 2);
|
||||
for (int i = 0; i < si.size(); i++) {
|
||||
String path;
|
||||
int line = 0;
|
||||
if (si[i].script.is_valid()) {
|
||||
path = si[i].script->get_path();
|
||||
line = si[i].line;
|
||||
}
|
||||
cstack[i * 2 + 0] = path;
|
||||
cstack[i * 2 + 1] = line;
|
||||
}
|
||||
|
||||
oe.callstack = cstack;
|
||||
|
||||
sdr->mutex->lock();
|
||||
|
||||
if (!sdr->locking && sdr->tcp_client->is_connected_to_host()) {
|
||||
|
||||
if (sdr->errors.size() >= sdr->max_errors_per_frame) {
|
||||
sdr->n_errors_dropped++;
|
||||
} else {
|
||||
sdr->errors.push_back(oe);
|
||||
}
|
||||
}
|
||||
|
||||
sdr->mutex->unlock();
|
||||
ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
|
||||
sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
|
||||
}
|
||||
|
||||
bool ScriptDebuggerRemote::_parse_live_edit(const Array &p_command) {
|
||||
|
@ -928,6 +888,45 @@ void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_
|
|||
mutex->unlock();
|
||||
}
|
||||
|
||||
void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
|
||||
|
||||
OutputError oe;
|
||||
oe.error = p_err;
|
||||
oe.error_descr = p_descr;
|
||||
oe.source_file = p_file;
|
||||
oe.source_line = p_line;
|
||||
oe.source_func = p_func;
|
||||
oe.warning = p_type == ERR_HANDLER_WARNING;
|
||||
uint64_t time = OS::get_singleton()->get_ticks_msec();
|
||||
oe.hr = time / 3600000;
|
||||
oe.min = (time / 60000) % 60;
|
||||
oe.sec = (time / 1000) % 60;
|
||||
oe.msec = time % 1000;
|
||||
Array cstack;
|
||||
|
||||
cstack.resize(p_stack_info.size() * 3);
|
||||
for (int i = 0; i < p_stack_info.size(); i++) {
|
||||
cstack[i * 3 + 0] = p_stack_info[i].file;
|
||||
cstack[i * 3 + 1] = p_stack_info[i].func;
|
||||
cstack[i * 3 + 2] = p_stack_info[i].line;
|
||||
}
|
||||
|
||||
oe.callstack = cstack;
|
||||
|
||||
mutex->lock();
|
||||
|
||||
if (!locking && tcp_client->is_connected_to_host()) {
|
||||
|
||||
if (errors.size() >= max_errors_per_frame) {
|
||||
n_errors_dropped++;
|
||||
} else {
|
||||
errors.push_back(oe);
|
||||
}
|
||||
}
|
||||
|
||||
mutex->unlock();
|
||||
}
|
||||
|
||||
void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) {
|
||||
|
||||
ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this;
|
||||
|
|
|
@ -157,6 +157,7 @@ public:
|
|||
virtual void request_quit();
|
||||
|
||||
virtual void send_message(const String &p_message, const Array &p_args);
|
||||
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
|
||||
|
||||
virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata);
|
||||
virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs);
|
||||
|
|
|
@ -254,7 +254,8 @@ public:
|
|||
virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) = 0;
|
||||
|
||||
struct StackInfo {
|
||||
Ref<Script> script;
|
||||
String file;
|
||||
String func;
|
||||
int line;
|
||||
};
|
||||
|
||||
|
@ -391,6 +392,7 @@ public:
|
|||
ScriptLanguage *get_break_language() const;
|
||||
|
||||
virtual void send_message(const String &p_message, const Array &p_args) = 0;
|
||||
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) = 0;
|
||||
|
||||
virtual bool is_remote() const { return false; }
|
||||
virtual void request_quit() {}
|
||||
|
|
|
@ -313,24 +313,22 @@ void ScriptEditor::_goto_script_line2(int p_line) {
|
|||
|
||||
void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
|
||||
|
||||
editor->push_item(p_script.ptr());
|
||||
Ref<Script> script = Object::cast_to<Script>(*p_script);
|
||||
if (!script.is_null() && script->get_path().is_resource_file()) {
|
||||
if (edit(p_script, p_line, 0)) {
|
||||
editor->push_item(p_script.ptr());
|
||||
|
||||
if (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
|
||||
int selected = tab_container->get_current_tab();
|
||||
if (selected < 0 || selected >= tab_container->get_child_count())
|
||||
return;
|
||||
|
||||
Ref<Script> script = Object::cast_to<Script>(*p_script);
|
||||
if (!script.is_null() && script->get_path().is_resource_file())
|
||||
edit(p_script, p_line, 0);
|
||||
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
current->goto_line(p_line, true);
|
||||
}
|
||||
}
|
||||
|
||||
int selected = tab_container->get_current_tab();
|
||||
if (selected < 0 || selected >= tab_container->get_child_count())
|
||||
return;
|
||||
|
||||
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
current->goto_line(p_line, true);
|
||||
}
|
||||
|
||||
void ScriptEditor::_update_history_arrows() {
|
||||
|
|
|
@ -1616,30 +1616,33 @@ void ScriptEditorDebugger::_error_selected(int p_idx) {
|
|||
|
||||
error_stack->clear();
|
||||
Array st = error_list->get_item_metadata(p_idx);
|
||||
for (int i = 0; i < st.size(); i += 2) {
|
||||
for (int i = 0; i < st.size(); i += 3) {
|
||||
|
||||
String script = st[i];
|
||||
int line = st[i + 1];
|
||||
String func = st[i + 1];
|
||||
int line = st[i + 2];
|
||||
Array md;
|
||||
md.push_back(st[i]);
|
||||
md.push_back(st[i + 1]);
|
||||
md.push_back(st[i + 2]);
|
||||
|
||||
String str = script.get_file() + ":" + itos(line);
|
||||
String str = func + " in " + script.get_file() + ":line " + itos(line);
|
||||
|
||||
error_stack->add_item(str);
|
||||
error_stack->set_item_metadata(error_stack->get_item_count() - 1, md);
|
||||
error_stack->set_item_tooltip(error_stack->get_item_count() - 1, TTR("File:") + " " + String(st[i]) + "\n" + TTR("Line:") + " " + itos(line));
|
||||
error_stack->set_item_tooltip(error_stack->get_item_count() - 1,
|
||||
TTR("File:") + " " + script + "\n" + TTR("Function:") + " " + func + "\n" + TTR("Line:") + " " + itos(line));
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_error_stack_selected(int p_idx) {
|
||||
|
||||
Array arr = error_stack->get_item_metadata(p_idx);
|
||||
if (arr.size() != 2)
|
||||
if (arr.size() != 3)
|
||||
return;
|
||||
|
||||
Ref<Script> s = ResourceLoader::load(arr[0]);
|
||||
emit_signal("goto_script_line", s, int(arr[1]) - 1);
|
||||
emit_signal("goto_script_line", s, int(arr[2]) - 1);
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::set_hide_on_stop(bool p_hide) {
|
||||
|
|
|
@ -349,7 +349,9 @@ public:
|
|||
csi.resize(_debug_call_stack_pos);
|
||||
for (int i = 0; i < _debug_call_stack_pos; i++) {
|
||||
csi[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0;
|
||||
csi[_debug_call_stack_pos - i - 1].script = Ref<GDScript>(_call_stack[i].function->get_script());
|
||||
if (_call_stack[i].function)
|
||||
csi[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name();
|
||||
csi[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path();
|
||||
}
|
||||
return csi;
|
||||
}
|
||||
|
|
|
@ -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_editor_tools_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) {
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
77
modules/mono/glue/cs_files/DebuggingUtils.cs
Normal file
77
modules/mono/glue/cs_files/DebuggingUtils.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_fail_silently) {
|
||||
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_fail_silently) {
|
||||
// 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)
|
||||
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
|
||||
|
|
|
@ -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_fail_silently);
|
||||
|
||||
} // 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
|
||||
|
|
Loading…
Reference in a new issue