Преглед изворни кода

Mono: Add option to keep running after unhandled exceptions

By default, an unhandled exception will cause the application to be terminated; but the project setting `mono/unhandled_exception_policy` was added to change this behaviour.
The editor is hard-coded to never terminate because of unhandled exceptions, as that would make writing editor plugins a painful task, and we cannot kill the editor because of a mistake in a thirdparty plugin.
Ignacio Etcheverry пре 6 година
родитељ
комит
513cc78f85

+ 0 - 1
modules/mono/csharp_script.cpp

@@ -629,7 +629,6 @@ void CSharpLanguage::frame() {
 
 
 				if (exc) {
 				if (exc) {
 					GDMonoUtils::debug_unhandled_exception(exc);
 					GDMonoUtils::debug_unhandled_exception(exc);
-					GD_UNREACHABLE();
 				}
 				}
 			}
 			}
 		}
 		}

+ 14 - 0
modules/mono/mono_gd/gd_mono.cpp

@@ -283,6 +283,18 @@ void GDMono::initialize() {
 
 
 	add_mono_shared_libs_dir_to_path();
 	add_mono_shared_libs_dir_to_path();
 
 
+	{
+		PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
+				vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
+		unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
+		ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
+
+		if (Engine::get_singleton()->is_editor_hint()) {
+			// Unhandled exceptions should not terminate the editor
+			unhandled_exception_policy = POLICY_LOG_ERROR;
+		}
+	}
+
 	GDMonoAssembly::initialize();
 	GDMonoAssembly::initialize();
 
 
 	gdmono_profiler_init();
 	gdmono_profiler_init();
@@ -1063,6 +1075,8 @@ GDMono::GDMono() {
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	api_editor_hash = 0;
 	api_editor_hash = 0;
 #endif
 #endif
+
+	unhandled_exception_policy = POLICY_TERMINATE_APP;
 }
 }
 
 
 GDMono::~GDMono() {
 GDMono::~GDMono() {

+ 12 - 1
modules/mono/mono_gd/gd_mono.h

@@ -80,6 +80,13 @@ String to_string(Type p_type);
 
 
 class GDMono {
 class GDMono {
 
 
+public:
+	enum UnhandledExceptionPolicy {
+		POLICY_TERMINATE_APP,
+		POLICY_LOG_ERROR
+	};
+
+private:
 	bool runtime_initialized;
 	bool runtime_initialized;
 	bool finalizing_scripts_domain;
 	bool finalizing_scripts_domain;
 
 
@@ -102,6 +109,8 @@ class GDMono {
 
 
 	HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
 	HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
 
 
+	UnhandledExceptionPolicy unhandled_exception_policy;
+
 	void _domain_assemblies_cleanup(uint32_t p_domain_id);
 	void _domain_assemblies_cleanup(uint32_t p_domain_id);
 
 
 	bool _are_api_assemblies_out_of_sync();
 	bool _are_api_assemblies_out_of_sync();
@@ -162,7 +171,9 @@ public:
 
 
 	static GDMono *get_singleton() { return singleton; }
 	static GDMono *get_singleton() { return singleton; }
 
 
-	static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+	GD_NORETURN static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
+
+	UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
 
 
 	// Do not use these, unless you know what you're doing
 	// Do not use these, unless you know what you're doing
 	void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);
 	void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly);

+ 12 - 3
modules/mono/mono_gd/gd_mono_internals.cpp

@@ -108,9 +108,18 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
 
 
 void unhandled_exception(MonoException *p_exc) {
 void unhandled_exception(MonoException *p_exc) {
 	mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
 	mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
-	// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
-	GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
-	GD_UNREACHABLE();
+
+	if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
+		// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
+		GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
+		GD_UNREACHABLE();
+	} else {
+#ifdef DEBUG_ENABLED
+		GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
+		if (ScriptDebugger::get_singleton())
+			ScriptDebugger::get_singleton()->idle_poll();
+#endif
+	}
 }
 }
 
 
 } // namespace GDMonoInternals
 } // namespace GDMonoInternals

+ 1 - 1
modules/mono/mono_gd/gd_mono_internals.h

@@ -45,7 +45,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
  * Do not call this function directly.
  * Do not call this function directly.
  * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
  * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
  */
  */
-GD_NORETURN void unhandled_exception(MonoException *p_exc);
+void unhandled_exception(MonoException *p_exc);
 
 
 } // namespace GDMonoInternals
 } // namespace GDMonoInternals
 
 

+ 12 - 5
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -37,6 +37,10 @@
 #include "core/project_settings.h"
 #include "core/project_settings.h"
 #include "core/reference.h"
 #include "core/reference.h"
 
 
+#ifdef TOOLS_ENABLED
+#include "editor/script_editor_debugger.h"
+#endif
+
 #include "../csharp_script.h"
 #include "../csharp_script.h"
 #include "../utils/macros.h"
 #include "../utils/macros.h"
 #include "../utils/mutex_utils.h"
 #include "../utils/mutex_utils.h"
@@ -596,8 +600,14 @@ void debug_print_unhandled_exception(MonoException *p_exc) {
 
 
 void debug_send_unhandled_exception_error(MonoException *p_exc) {
 void debug_send_unhandled_exception_error(MonoException *p_exc) {
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
-	if (!ScriptDebugger::get_singleton())
+	if (!ScriptDebugger::get_singleton()) {
+#ifdef TOOLS_ENABLED
+		if (Engine::get_singleton()->is_editor_hint()) {
+			ERR_PRINTS(GDMonoUtils::get_exception_name_and_message(p_exc));
+		}
+#endif
 		return;
 		return;
+	}
 
 
 	_TLS_RECURSION_GUARD_;
 	_TLS_RECURSION_GUARD_;
 
 
@@ -621,7 +631,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
 
 
 		if (unexpected_exc) {
 		if (unexpected_exc) {
 			GDMonoInternals::unhandled_exception(unexpected_exc);
 			GDMonoInternals::unhandled_exception(unexpected_exc);
-			GD_UNREACHABLE();
+			return;
 		}
 		}
 
 
 		Vector<ScriptLanguage::StackInfo> _si;
 		Vector<ScriptLanguage::StackInfo> _si;
@@ -655,7 +665,6 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
 
 
 void debug_unhandled_exception(MonoException *p_exc) {
 void debug_unhandled_exception(MonoException *p_exc) {
 	GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
 	GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
-	GD_UNREACHABLE();
 }
 }
 
 
 void print_unhandled_exception(MonoException *p_exc) {
 void print_unhandled_exception(MonoException *p_exc) {
@@ -665,11 +674,9 @@ void print_unhandled_exception(MonoException *p_exc) {
 void set_pending_exception(MonoException *p_exc) {
 void set_pending_exception(MonoException *p_exc) {
 #ifdef NO_PENDING_EXCEPTIONS
 #ifdef NO_PENDING_EXCEPTIONS
 	debug_unhandled_exception(p_exc);
 	debug_unhandled_exception(p_exc);
-	GD_UNREACHABLE();
 #else
 #else
 	if (get_runtime_invoke_count() == 0) {
 	if (get_runtime_invoke_count() == 0) {
 		debug_unhandled_exception(p_exc);
 		debug_unhandled_exception(p_exc);
-		GD_UNREACHABLE();
 	}
 	}
 
 
 	if (!mono_runtime_set_pending_exception(p_exc, false)) {
 	if (!mono_runtime_set_pending_exception(p_exc, false)) {

+ 1 - 1
modules/mono/mono_gd/gd_mono_utils.h

@@ -289,7 +289,7 @@ void set_exception_message(MonoException *p_exc, String message);
 
 
 void debug_print_unhandled_exception(MonoException *p_exc);
 void debug_print_unhandled_exception(MonoException *p_exc);
 void debug_send_unhandled_exception_error(MonoException *p_exc);
 void debug_send_unhandled_exception_error(MonoException *p_exc);
-GD_NORETURN void debug_unhandled_exception(MonoException *p_exc);
+void debug_unhandled_exception(MonoException *p_exc);
 void print_unhandled_exception(MonoException *p_exc);
 void print_unhandled_exception(MonoException *p_exc);
 
 
 /**
 /**