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:
commit
ad0d87b4dd
10 changed files with 78 additions and 24 deletions
|
@ -629,7 +629,6 @@ void CSharpLanguage::frame() {
|
||||||
|
|
||||||
if (exc) {
|
if (exc) {
|
||||||
GDMonoUtils::debug_unhandled_exception(exc);
|
GDMonoUtils::debug_unhandled_exception(exc);
|
||||||
GD_UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,7 +298,16 @@ namespace GodotTools
|
||||||
if (line >= 0)
|
if (line >= 0)
|
||||||
scriptPath += $";{line + 1};{col}";
|
scriptPath += $";{line + 1};{col}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath);
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
|
||||||
// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
|
// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
|
||||||
GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
|
GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
|
||||||
GD_UNREACHABLE();
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue