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

Unhandled exception policy and fix external editors on Windows
This commit is contained in:
Ignacio Roldán Etcheverry 2019-07-25 00:24:35 +02:00 committed by GitHub
commit ad0d87b4dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 78 additions and 24 deletions

View file

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

View file

@ -298,7 +298,16 @@ namespace GodotTools
if (line >= 0) if (line >= 0)
scriptPath += $";{line + 1};{col}"; 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; break;
} }

View file

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

View file

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

View file

@ -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() {

View file

@ -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);

View file

@ -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); if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
GD_UNREACHABLE(); // 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

View file

@ -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

View file

@ -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)) {

View file

@ -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);
/** /**