Răsfoiți Sursa

Merge pull request #30802 from neikeq/exc-policy-and-issue-30519

Unhandled exception policy and fix external editors on Windows
Ignacio Roldán Etcheverry 6 ani în urmă
părinte
comite
ad0d87b4dd

+ 0 - 1
modules/mono/csharp_script.cpp

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

+ 10 - 1
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -298,7 +298,16 @@ namespace GodotTools
                     if (line >= 0)
                         scriptPath += $";{line + 1};{col}";
 
-                    GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath);
+                    try
+                    {
+                        GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath);
+                    }
+                    catch (FileNotFoundException)
+                    {
+                        string editorName = editor == ExternalEditor.VisualStudioForMac ? "Visual Studio" : "MonoDevelop";
+                        GD.PushError($"Cannot find code editor: {editorName}");
+                        return Error.FileNotFound;
+                    }
 
                     break;
                 }

+ 13 - 9
modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs

@@ -4,6 +4,7 @@ using System.IO;
 using System.Collections.Generic;
 using System.Diagnostics;
 using GodotTools.Internals;
+using GodotTools.Utils;
 
 namespace GodotTools
 {
@@ -30,7 +31,7 @@ namespace GodotTools
 
             if (Utils.OS.IsOSX())
             {
-                string bundleId = CodeEditorBundleIds[editorId];
+                string bundleId = BundleIds[editorId];
 
                 if (Internal.IsOsxAppBundleInstalled(bundleId))
                 {
@@ -47,12 +48,12 @@ namespace GodotTools
                 }
                 else
                 {
-                    command = CodeEditorPaths[editorId];
+                    command = OS.PathWhich(ExecutableNames[editorId]);
                 }
             }
             else
             {
-                command = CodeEditorPaths[editorId];
+                command = OS.PathWhich(ExecutableNames[editorId]);
             }
 
             args.Add("--ipc-tcp");
@@ -70,6 +71,9 @@ namespace GodotTools
                 args.Add("\"" + Path.GetFullPath(filePath.NormalizePath()) + cursor + "\"");
             }
 
+            if (command == null)
+                throw new FileNotFoundException();
+
             if (newWindow)
             {
                 process = Process.Start(new ProcessStartInfo
@@ -99,20 +103,20 @@ namespace GodotTools
             this.editorId = editorId;
         }
 
-        private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorPaths;
-        private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorBundleIds;
+        private static readonly IReadOnlyDictionary<EditorId, string> ExecutableNames;
+        private static readonly IReadOnlyDictionary<EditorId, string> BundleIds;
 
         static MonoDevelopInstance()
         {
             if (Utils.OS.IsOSX())
             {
-                CodeEditorPaths = new Dictionary<EditorId, string>
+                ExecutableNames = new Dictionary<EditorId, string>
                 {
                     // Rely on PATH
                     {EditorId.MonoDevelop, "monodevelop"},
                     {EditorId.VisualStudioForMac, "VisualStudio"}
                 };
-                CodeEditorBundleIds = new Dictionary<EditorId, string>
+                BundleIds = new Dictionary<EditorId, string>
                 {
                     // TODO EditorId.MonoDevelop
                     {EditorId.VisualStudioForMac, "com.microsoft.visual-studio"}
@@ -120,7 +124,7 @@ namespace GodotTools
             }
             else if (Utils.OS.IsWindows())
             {
-                CodeEditorPaths = new Dictionary<EditorId, string>
+                ExecutableNames = new Dictionary<EditorId, string>
                 {
                     // XamarinStudio is no longer a thing, and the latest version is quite old
                     // MonoDevelop is available from source only on Windows. The recommendation
@@ -131,7 +135,7 @@ namespace GodotTools
             }
             else if (Utils.OS.IsUnix())
             {
-                CodeEditorPaths = new Dictionary<EditorId, string>
+                ExecutableNames = new Dictionary<EditorId, string>
                 {
                     // Rely on PATH
                     {EditorId.MonoDevelop, "monodevelop"}

+ 3 - 2
modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs

@@ -10,8 +10,9 @@ namespace GodotTools.Utils
         {
             foreach (T elem in enumerable)
             {
-                if (predicate(elem) != null)
-                    return elem;
+                T result = predicate(elem);
+                if (result != null)
+                    return result;
             }
 
             return orElse;

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

@@ -283,6 +283,18 @@ void GDMono::initialize() {
 
 	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();
 
 	gdmono_profiler_init();
@@ -1063,6 +1075,8 @@ GDMono::GDMono() {
 #ifdef TOOLS_ENABLED
 	api_editor_hash = 0;
 #endif
+
+	unhandled_exception_policy = POLICY_TERMINATE_APP;
 }
 
 GDMono::~GDMono() {

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

@@ -80,6 +80,13 @@ String to_string(Type p_type);
 
 class GDMono {
 
+public:
+	enum UnhandledExceptionPolicy {
+		POLICY_TERMINATE_APP,
+		POLICY_LOG_ERROR
+	};
+
+private:
 	bool runtime_initialized;
 	bool finalizing_scripts_domain;
 
@@ -102,6 +109,8 @@ class GDMono {
 
 	HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
 
+	UnhandledExceptionPolicy unhandled_exception_policy;
+
 	void _domain_assemblies_cleanup(uint32_t p_domain_id);
 
 	bool _are_api_assemblies_out_of_sync();
@@ -162,7 +171,9 @@ public:
 
 	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
 	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) {
 	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

+ 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.
  * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
  */
-GD_NORETURN void unhandled_exception(MonoException *p_exc);
+void unhandled_exception(MonoException *p_exc);
 
 } // namespace GDMonoInternals
 

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

@@ -37,6 +37,10 @@
 #include "core/project_settings.h"
 #include "core/reference.h"
 
+#ifdef TOOLS_ENABLED
+#include "editor/script_editor_debugger.h"
+#endif
+
 #include "../csharp_script.h"
 #include "../utils/macros.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) {
 #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;
+	}
 
 	_TLS_RECURSION_GUARD_;
 
@@ -621,7 +631,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
 
 		if (unexpected_exc) {
 			GDMonoInternals::unhandled_exception(unexpected_exc);
-			GD_UNREACHABLE();
+			return;
 		}
 
 		Vector<ScriptLanguage::StackInfo> _si;
@@ -655,7 +665,6 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
 
 void debug_unhandled_exception(MonoException *p_exc) {
 	GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
-	GD_UNREACHABLE();
 }
 
 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) {
 #ifdef NO_PENDING_EXCEPTIONS
 	debug_unhandled_exception(p_exc);
-	GD_UNREACHABLE();
 #else
 	if (get_runtime_invoke_count() == 0) {
 		debug_unhandled_exception(p_exc);
-		GD_UNREACHABLE();
 	}
 
 	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_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);
 
 /**