Mono: Pending exceptions and cleanup

This commit is contained in:
Ignacio Etcheverry 2018-06-26 21:03:42 +02:00
parent 68f7cf13c7
commit 4739cb8c00
23 changed files with 720 additions and 193 deletions

View file

@ -5,9 +5,11 @@ Import('env_modules')
env_mono = env_modules.Clone() env_mono = env_modules.Clone()
from compat import byte_to_str # TODO move functions to their own modules
def make_cs_files_header(src, dst): def make_cs_files_header(src, dst):
from compat import byte_to_str
with open(dst, 'w') as header: with open(dst, 'w') as header:
header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n') header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n')
header.write('#ifndef _CS_FILES_DATA_H\n') header.write('#ifndef _CS_FILES_DATA_H\n')
@ -75,6 +77,13 @@ else:
if ARGUMENTS.get('yolo_copy', False): if ARGUMENTS.get('yolo_copy', False):
env_mono.Append(CPPDEFINES=['YOLO_COPY']) env_mono.Append(CPPDEFINES=['YOLO_COPY'])
# Configure TLS checks
import tls_configure
conf = Configure(env_mono)
tls_configure.configure(conf)
env_mono = conf.Finish()
# Build GodotSharpTools solution # Build GodotSharpTools solution
@ -128,12 +137,13 @@ def find_msbuild_windows():
raise RuntimeError('Cannot find mono root directory') raise RuntimeError('Cannot find mono root directory')
framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5')
mono_bin_dir = os.path.join(mono_root, 'bin') mono_bin_dir = os.path.join(mono_root, 'bin')
msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat')
if os.path.isfile(msbuild_mono): if os.path.isfile(msbuild_mono):
# The (Csc/Vbc/Fsc)ToolExe environment variables are required when
# building with Mono's MSBuild. They must point to the batch files
# in Mono's bin directory to make sure they are executed with Mono.
mono_msbuild_env = { mono_msbuild_env = {
'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'),
'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'),

View file

@ -4,12 +4,36 @@ import os
import sys import sys
import subprocess import subprocess
from distutils.version import LooseVersion
from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables
monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py')
def can_build(env, platform):
if platform in ['javascript']:
return False # Not yet supported
return True
def is_enabled():
# The module is disabled by default. Use module_mono_enabled=yes to enable it.
return False
def get_doc_classes():
return [
'@C#',
'CSharpScript',
'GodotSharp',
]
def get_doc_path():
return 'doc_classes'
def find_file_in_dir(directory, files, prefix='', extension=''): def find_file_in_dir(directory, files, prefix='', extension=''):
if not extension.startswith('.'): if not extension.startswith('.'):
extension = '.' + extension extension = '.' + extension
@ -19,17 +43,6 @@ def find_file_in_dir(directory, files, prefix='', extension=''):
return '' return ''
def can_build(env, platform):
if platform in ["javascript"]:
return False # Not yet supported
return True
def is_enabled():
# The module is disabled by default. Use module_mono_enabled=yes to enable it.
return False
def copy_file(src_dir, dst_dir, name): def copy_file(src_dir, dst_dir, name):
from shutil import copyfile from shutil import copyfile
@ -55,7 +68,7 @@ def custom_path_is_dir_create(key, val, env):
def configure(env): def configure(env):
env.use_ptrcall = True env.use_ptrcall = True
env.add_module_version_string("mono") env.add_module_version_string('mono')
envvars = Variables() envvars = Variables()
envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) envvars.Add(BoolVariable('mono_static', 'Statically link mono', False))
@ -84,6 +97,9 @@ def configure(env):
if not mono_root: if not mono_root:
raise RuntimeError('Mono installation directory not found') raise RuntimeError('Mono installation directory not found')
mono_version = mono_root_try_find_mono_version(mono_root)
configure_for_mono_version(env, mono_version)
mono_lib_path = os.path.join(mono_root, 'lib') mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path) env.Append(LIBPATH=mono_lib_path)
@ -149,20 +165,14 @@ def configure(env):
# We can't use pkg-config to link mono statically, # We can't use pkg-config to link mono statically,
# but we can still use it to find the mono root directory # but we can still use it to find the mono root directory
if not mono_root and mono_static: if not mono_root and mono_static:
def pkgconfig_try_find_mono_root(): mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
for hint_dir in tmpenv['LIBPATH']:
name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
return os.path.join(hint_dir, '..')
return ''
mono_root = pkgconfig_try_find_mono_root()
if not mono_root: if not mono_root:
raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually')
if mono_root: if mono_root:
mono_version = mono_root_try_find_mono_version(mono_root)
configure_for_mono_version(env, mono_version)
mono_lib_path = os.path.join(mono_root, 'lib') mono_lib_path = os.path.join(mono_root, 'lib')
env.Append(LIBPATH=mono_lib_path) env.Append(LIBPATH=mono_lib_path)
@ -178,18 +188,18 @@ def configure(env):
if mono_static: if mono_static:
mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a') mono_lib_file = os.path.join(mono_lib_path, 'lib' + mono_lib + '.a')
if sys.platform == "darwin": if sys.platform == 'darwin':
env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file]) env.Append(LINKFLAGS=['-Wl,-force_load,' + mono_lib_file])
elif sys.platform == "linux" or sys.platform == "linux2": elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive']) env.Append(LINKFLAGS=['-Wl,-whole-archive', mono_lib_file, '-Wl,-no-whole-archive'])
else: else:
raise RuntimeError('mono-static: Not supported on this platform') raise RuntimeError('mono-static: Not supported on this platform')
else: else:
env.Append(LIBS=[mono_lib]) env.Append(LIBS=[mono_lib])
if sys.platform == "darwin": if sys.platform == 'darwin':
env.Append(LIBS=['iconv', 'pthread']) env.Append(LIBS=['iconv', 'pthread'])
elif sys.platform == "linux" or sys.platform == "linux2": elif sys.platform == 'linux' or sys.platform == 'linux2':
env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
if not mono_static: if not mono_static:
@ -204,11 +214,14 @@ def configure(env):
else: else:
assert not mono_static assert not mono_static
mono_version = pkgconfig_try_find_mono_version()
configure_for_mono_version(env, mono_version)
env.ParseConfig('pkg-config monosgen-2 --cflags --libs') env.ParseConfig('pkg-config monosgen-2 --cflags --libs')
mono_lib_path = '' mono_lib_path = ''
mono_so_name = '' mono_so_name = ''
mono_prefix = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip() mono_prefix = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
tmpenv = Environment() tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@ -230,13 +243,41 @@ def configure(env):
env.Append(LINKFLAGS='-rdynamic') env.Append(LINKFLAGS='-rdynamic')
def get_doc_classes(): def configure_for_mono_version(env, mono_version):
return [ if mono_version is None:
"@C#", raise RuntimeError('Mono JIT compiler version not found')
"CSharpScript", print('Mono JIT compiler version: ' + str(mono_version))
"GodotSharp", if mono_version >= LooseVersion("5.12.0"):
] env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS'])
def get_doc_path(): def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
return "doc_classes" tmpenv = Environment()
tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
tmpenv.ParseConfig('pkg-config monosgen-2 --libs-only-L')
for hint_dir in tmpenv['LIBPATH']:
name_found = find_file_in_dir(hint_dir, mono_lib_names, prefix='lib', extension=sharedlib_ext)
if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
return os.path.join(hint_dir, '..')
return ''
def pkgconfig_try_find_mono_version():
lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
greater_version = None
for line in lines:
try:
version = LooseVersion(line)
if greater_version is None or version > greater_version:
greater_version = version
except ValueError:
pass
return greater_version
def mono_root_try_find_mono_version(mono_root):
first_line = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version']).splitlines()[0]
try:
return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
except (ValueError, IndexError):
return None

View file

@ -49,6 +49,8 @@
#include "mono_gd/gd_mono_class.h" #include "mono_gd/gd_mono_class.h"
#include "mono_gd/gd_mono_marshal.h" #include "mono_gd/gd_mono_marshal.h"
#include "signal_awaiter_utils.h" #include "signal_awaiter_utils.h"
#include "utils/macros.h"
#include "utils/thread_local.h"
#define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
@ -476,7 +478,7 @@ String CSharpLanguage::_get_indentation() const {
Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
// Printing an error here will result in endless recursion, so we must be careful _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated) if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
return Vector<StackInfo>(); return Vector<StackInfo>();
@ -500,15 +502,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) { 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 _TLS_RECURSION_GUARD_V_(Vector<StackInfo>());
MonoObject *exc = NULL; MonoException *exc = NULL;
GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames); GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames);
MonoArray *frames = st_get_frames(p_stack_trace, &exc); MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc);
if (exc) { if (exc) {
GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>(); return Vector<StackInfo>();
} }
@ -529,10 +531,10 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
MonoString *file_name; MonoString *file_name;
int file_line_num; int file_line_num;
MonoString *method_decl; MonoString *method_decl;
get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc); get_sf_info(frame, &file_name, &file_line_num, &method_decl, (MonoObject **)&exc);
if (exc) { if (exc) {
GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); GDMonoUtils::debug_print_unhandled_exception(exc);
return Vector<StackInfo>(); return Vector<StackInfo>();
} }
@ -561,12 +563,12 @@ void CSharpLanguage::frame() {
ERR_FAIL_NULL(thunk); ERR_FAIL_NULL(thunk);
MonoObject *ex; MonoException *exc = NULL;
thunk(task_scheduler, &ex); thunk(task_scheduler, (MonoObject **)&exc);
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL(); _UNREACHABLE_();
} }
} }
} }
@ -1078,11 +1080,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
GDMonoProperty *property = top->get_property(p_name); GDMonoProperty *property = top->get_property(p_name);
if (property) { if (property) {
MonoObject *exc = NULL; MonoException *exc = NULL;
MonoObject *value = property->get_value(mono_object, &exc); MonoObject *value = property->get_value(mono_object, &exc);
if (exc) { if (exc) {
r_ret = Variant(); r_ret = Variant();
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::set_pending_exception(exc);
} else { } else {
r_ret = GDMonoMarshal::mono_object_to_variant(value); r_ret = GDMonoMarshal::mono_object_to_variant(value);
} }
@ -1490,12 +1492,12 @@ bool CSharpScript::_update_exports() {
CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround CACHED_FIELD(GodotObject, ptr)->set_value_raw(tmp_object, tmp_object); // FIXME WTF is this workaround
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0); GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
MonoObject *ex = NULL; MonoException *exc = NULL;
ctor->invoke(tmp_object, NULL, &ex); ctor->invoke(tmp_object, NULL, &exc);
if (ex) { if (exc) {
ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
mono_print_unhandled_exception(ex); GDMonoUtils::debug_print_unhandled_exception(exc);
tmp_object = NULL; tmp_object = NULL;
ERR_FAIL_V(false); ERR_FAIL_V(false);
} }
@ -1544,11 +1546,11 @@ bool CSharpScript::_update_exports() {
exported_members_cache.push_front(prop_info); exported_members_cache.push_front(prop_info);
if (tmp_object) { if (tmp_object) {
MonoObject *exc = NULL; MonoException *exc = NULL;
MonoObject *ret = property->get_value(tmp_object, &exc); MonoObject *ret = property->get_value(tmp_object, &exc);
if (exc) { if (exc) {
exported_members_defval_cache[name] = Variant(); exported_members_defval_cache[name] = Variant();
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::debug_print_unhandled_exception(exc);
} else { } else {
exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret); exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
} }
@ -1918,7 +1920,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Construct // Construct
GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount); GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
ctor->invoke(mono_object, p_args, NULL); ctor->invoke(mono_object, p_args);
// Tie managed to unmanaged // Tie managed to unmanaged
instance->gchandle = MonoGCHandle::create_strong(mono_object); instance->gchandle = MonoGCHandle::create_strong(mono_object);

View file

@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi
Variant dir = p_dir; Variant dir = p_dir;
Variant compile_items = p_files; Variant compile_items = p_files;
const Variant *args[2] = { &dir, &compile_items }; const Variant *args[2] = { &dir, &compile_items };
MonoObject *ex = NULL; MonoException *exc = NULL;
MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex); MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String()); ERR_FAIL_V(String());
} }
@ -68,11 +68,11 @@ String generate_editor_api_project(const String &p_dir, const String &p_core_dll
Variant core_dll_path = p_core_dll_path; Variant core_dll_path = p_core_dll_path;
Variant compile_items = p_files; Variant compile_items = p_files;
const Variant *args[3] = { &dir, &core_dll_path, &compile_items }; const Variant *args[3] = { &dir, &core_dll_path, &compile_items };
MonoObject *ex = NULL; MonoException *exc = NULL;
MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex); MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String()); ERR_FAIL_V(String());
} }
@ -89,11 +89,11 @@ String generate_game_project(const String &p_dir, const String &p_name, const Ve
Variant name = p_name; Variant name = p_name;
Variant compile_items = p_files; Variant compile_items = p_files;
const Variant *args[3] = { &dir, &name, &compile_items }; const Variant *args[3] = { &dir, &name, &compile_items };
MonoObject *ex = NULL; MonoException *exc = NULL;
MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex); MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL_V(String()); ERR_FAIL_V(String());
} }
@ -110,11 +110,11 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
Variant item_type = p_item_type; Variant item_type = p_item_type;
Variant include = p_include; Variant include = p_include;
const Variant *args[3] = { &project_path, &item_type, &include }; const Variant *args[3] = { &project_path, &item_type, &include };
MonoObject *ex = NULL; MonoException *exc = NULL;
klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex); klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc);
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL(); ERR_FAIL();
} }
} }

View file

@ -512,14 +512,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *ctor_args[2] = { &solution, &config }; const Variant *ctor_args[2] = { &solution, &config };
MonoObject *ex = NULL; MonoException *exc = NULL;
GDMonoMethod *ctor = klass->get_method(".ctor", 2); GDMonoMethod *ctor = klass->get_method(".ctor", 2);
ctor->invoke(mono_object, ctor_args, &ex); ctor->invoke(mono_object, ctor_args, &exc);
if (ex) { if (exc) {
exited = true; exited = true;
GDMonoUtils::print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message); build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message); ERR_EXPLAIN(message);
ERR_FAIL(); ERR_FAIL();
@ -534,14 +534,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props }; const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
ex = NULL; exc = NULL;
GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3); GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
build_method->invoke(mono_object, args, &ex); build_method->invoke(mono_object, args, &exc);
if (ex) { if (exc) {
exited = true; exited = true;
GDMonoUtils::print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
build_tab->on_build_exec_failed(message); build_tab->on_build_exec_failed(message);
ERR_EXPLAIN(message); ERR_EXPLAIN(message);
ERR_FAIL(); ERR_FAIL();

View file

@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) {
ERR_FAIL_NULL(execute_method); ERR_FAIL_NULL(execute_method);
ERR_FAIL_COND(gc_handle.is_null()); ERR_FAIL_COND(gc_handle.is_null());
MonoObject *ex = NULL; MonoException *exc = NULL;
Variant files = p_files; Variant files = p_files;
const Variant *args[1] = { &files }; const Variant *args[1] = { &files };
execute_method->invoke(gc_handle->get_target(), args, &ex); execute_method->invoke(gc_handle->get_target(), args, &exc);
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL(); ERR_FAIL();
} }
} }
@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) {
MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
GDMonoMethod *ctor = klass->get_method(".ctor", 1); GDMonoMethod *ctor = klass->get_method(".ctor", 1);
MonoObject *ex = NULL; MonoException *exc = NULL;
Variant solution = p_solution; Variant solution = p_solution;
const Variant *args[1] = { &solution }; const Variant *args[1] = { &solution };
ctor->invoke(obj, args, &ex); ctor->invoke(obj, args, &exc);
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
ERR_FAIL(); ERR_FAIL();
} }

View file

@ -34,15 +34,12 @@
uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) { uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) {
return mono_gchandle_new( return mono_gchandle_new(p_object, /* pinned: */ false);
p_object,
false /* do not pin the object */
);
} }
uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) { uint32_t MonoGCHandle::make_weak_handle(MonoObject *p_object) {
return mono_gchandle_new_weakref(p_object, false); return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
} }
Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) { Ref<MonoGCHandle> MonoGCHandle::create_strong(MonoObject *p_object) {

View file

@ -53,14 +53,6 @@
#include "main/main.h" #include "main/main.h"
#endif #endif
void gdmono_unhandled_exception_hook(MonoObject *exc, void *user_data) {
(void)user_data; // UNUSED
GDMonoUtils::print_unhandled_exception(exc);
abort();
}
#ifdef MONO_PRINT_HANDLER_ENABLED #ifdef MONO_PRINT_HANDLER_ENABLED
void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) {
@ -197,6 +189,8 @@ void GDMono::initialize() {
mono_config_parse(NULL); mono_config_parse(NULL);
mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
ERR_EXPLAIN("Mono: Failed to initialize runtime"); ERR_EXPLAIN("Mono: Failed to initialize runtime");
@ -279,8 +273,6 @@ void GDMono::initialize() {
OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n"); OS::get_singleton()->print("Mono: Glue disabled, ignoring script assemblies\n");
#endif #endif
mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
OS::get_singleton()->print("Mono: INITIALIZED\n"); OS::get_singleton()->print("Mono: INITIALIZED\n");
} }
@ -652,12 +644,12 @@ Error GDMono::_unload_scripts_domain() {
_GodotSharp::get_singleton()->_dispose_callback(); _GodotSharp::get_singleton()->_dispose_callback();
MonoObject *ex = NULL; MonoException *exc = NULL;
mono_domain_try_unload(domain, &ex); mono_domain_try_unload(domain, (MonoObject **)&exc);
if (ex) { if (exc) {
ERR_PRINT("Exception thrown when unloading scripts domain:"); ERR_PRINT("Exception thrown when unloading scripts domain");
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
return FAILED; return FAILED;
} }
@ -763,12 +755,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
_domain_assemblies_cleanup(mono_domain_get_id(p_domain)); _domain_assemblies_cleanup(mono_domain_get_id(p_domain));
MonoObject *ex = NULL; MonoException *exc = NULL;
mono_domain_try_unload(p_domain, &ex); mono_domain_try_unload(p_domain, (MonoObject **)&exc);
if (ex) { if (exc) {
ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:"); ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
mono_print_unhandled_exception(ex); GDMonoUtils::debug_unhandled_exception(exc);
return FAILED; return FAILED;
} }
@ -811,6 +803,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
assemblies.erase(p_domain_id); assemblies.erase(p_domain_id);
} }
void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
// This method will be called by the runtime when a thrown exception is not handled.
// It won't be called when we manually treat a thrown exception as unhandled.
// We assume the exception was already printed before calling this hook.
#ifdef DEBUG_ENABLED
GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
if (ScriptDebugger::get_singleton())
ScriptDebugger::get_singleton()->idle_poll();
#endif
abort();
_UNREACHABLE_();
}
GDMono::GDMono() { GDMono::GDMono() {
singleton = this; singleton = this;

View file

@ -164,6 +164,8 @@ 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);
// 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);
GDMonoAssembly **get_loaded_assembly(const String &p_name); GDMonoAssembly **get_loaded_assembly(const String &p_name);

View file

@ -32,8 +32,12 @@
#include "../csharp_script.h" #include "../csharp_script.h"
#include "../mono_gc_handle.h" #include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "../utils/thread_local.h"
#include "gd_mono_utils.h" #include "gd_mono_utils.h"
#include <mono/metadata/exception.h>
namespace GDMonoInternals { namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
return; return;
} }
void unhandled_exception(MonoException *p_exc) {
mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well
abort();
_UNREACHABLE_();
}
} // namespace GDMonoInternals } // namespace GDMonoInternals

View file

@ -33,11 +33,20 @@
#include <mono/jit/jit.h> #include <mono/jit/jit.h>
#include "../utils/macros.h"
#include "core/object.h" #include "core/object.h"
namespace GDMonoInternals { namespace GDMonoInternals {
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged); void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
}
/**
* Do not call this function directly.
* Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
*/
_NO_RETURN_ void unhandled_exception(MonoException *p_exc);
} // namespace GDMonoInternals
#endif // GD_MONO_INTERNALS_H #endif // GD_MONO_INTERNALS_H

View file

@ -837,11 +837,13 @@ MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) {
GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary); GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary);
MonoObject *ex = NULL; MonoException *exc = NULL;
MonoObject *ret = arrays_to_dict(keys, values, &ex); GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(NULL); ERR_FAIL_V(NULL);
} }
@ -858,11 +860,13 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict) {
MonoArray *keys = NULL; MonoArray *keys = NULL;
MonoArray *values = NULL; MonoArray *values = NULL;
MonoObject *ex = NULL; MonoException *exc = NULL;
dict_to_arrays(p_dict, &keys, &values, &ex); GD_MONO_BEGIN_RUNTIME_INVOKE;
dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(Dictionary()); ERR_FAIL_V(Dictionary());
} }

View file

@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() {
return mono_method_get_unmanaged_thunk(mono_method); return mono_method_get_unmanaged_thunk(mono_method);
} }
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc) { MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) {
if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) { if (get_return_type().type_encoding != MONO_TYPE_VOID || get_parameters_count() > 0) {
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count()); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), get_parameters_count());
@ -104,28 +104,32 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
mono_array_set(params, MonoObject *, i, boxed_param); mono_array_set(params, MonoObject *, i, boxed_param);
} }
MonoObject *exc = NULL; MonoException *exc = NULL;
MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc); GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {
ret = NULL; ret = NULL;
if (r_exc) { if (r_exc) {
*r_exc = exc; *r_exc = exc;
} else { } else {
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::set_pending_exception(exc);
} }
} }
return ret; return ret;
} else { } else {
MonoObject *exc = NULL; MonoException *exc = NULL;
mono_runtime_invoke(mono_method, p_object, NULL, &exc); GD_MONO_BEGIN_RUNTIME_INVOKE;
mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {
if (r_exc) { if (r_exc) {
*r_exc = exc; *r_exc = exc;
} else { } else {
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::set_pending_exception(exc);
} }
} }
@ -133,21 +137,23 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
} }
} }
MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoObject **r_exc) { MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) {
ERR_FAIL_COND_V(get_parameters_count() > 0, NULL); ERR_FAIL_COND_V(get_parameters_count() > 0, NULL);
return invoke_raw(p_object, NULL, r_exc); return invoke_raw(p_object, NULL, r_exc);
} }
MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) { MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) {
MonoObject *exc = NULL; MonoException *exc = NULL;
MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc); GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {
ret = NULL; ret = NULL;
if (r_exc) { if (r_exc) {
*r_exc = exc; *r_exc = exc;
} else { } else {
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::set_pending_exception(exc);
} }
} }

View file

@ -71,9 +71,9 @@ public:
void *get_thunk(); void *get_thunk();
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL); MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL);
MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL); MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = NULL);
MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
String get_full_name(bool p_signature = false) const; String get_full_name(bool p_signature = false) const;
String get_full_name_no_class() const; String get_full_name_no_class() const;

View file

@ -138,47 +138,53 @@ bool GDMonoProperty::has_setter() {
return mono_property_get_set_method(mono_property) != NULL; return mono_property_get_set_method(mono_property) != NULL;
} }
void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc) { void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
MonoMethod *prop_method = mono_property_get_set_method(mono_property); MonoMethod *prop_method = mono_property_get_set_method(mono_property);
MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
mono_array_set(params, MonoObject *, 0, p_value); mono_array_set(params, MonoObject *, 0, p_value);
MonoObject *exc = NULL; MonoException *exc = NULL;
mono_runtime_invoke_array(prop_method, p_object, params, &exc); GD_MONO_BEGIN_RUNTIME_INVOKE;
mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {
if (r_exc) { if (r_exc) {
*r_exc = exc; *r_exc = exc;
} else { } else {
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::set_pending_exception(exc);
} }
} }
} }
void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) { void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
MonoObject *exc = NULL; MonoException *exc = NULL;
mono_property_set_value(mono_property, p_object, p_params, &exc); GD_MONO_BEGIN_RUNTIME_INVOKE;
mono_property_set_value(mono_property, p_object, p_params, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {
if (r_exc) { if (r_exc) {
*r_exc = exc; *r_exc = exc;
} else { } else {
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::set_pending_exception(exc);
} }
} }
} }
MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) { MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
MonoObject *exc = NULL; MonoException *exc = NULL;
MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc); GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (exc) { if (exc) {
ret = NULL; ret = NULL;
if (r_exc) { if (r_exc) {
*r_exc = exc; *r_exc = exc;
} else { } else {
GDMonoUtils::print_unhandled_exception(exc); GDMonoUtils::set_pending_exception(exc);
} }
} }

View file

@ -62,9 +62,9 @@ public:
_FORCE_INLINE_ ManagedType get_type() const { return type; } _FORCE_INLINE_ ManagedType get_type() const { return type; }
void set_value(MonoObject *p_object, MonoObject *p_value, MonoObject **r_exc = NULL); void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = NULL);
void set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = NULL);
MonoObject *get_value(MonoObject *p_object, MonoObject **r_exc = NULL); MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL);
bool get_bool_value(MonoObject *p_object); bool get_bool_value(MonoObject *p_object);
int get_int_value(MonoObject *p_object); int get_int_value(MonoObject *p_object);

View file

@ -30,12 +30,15 @@
#include "gd_mono_utils.h" #include "gd_mono_utils.h"
#include <mono/metadata/exception.h>
#include "os/dir_access.h" #include "os/dir_access.h"
#include "os/os.h" #include "os/os.h"
#include "project_settings.h" #include "project_settings.h"
#include "reference.h" #include "reference.h"
#include "../csharp_script.h" #include "../csharp_script.h"
#include "../utils/macros.h"
#include "gd_mono.h" #include "gd_mono.h"
#include "gd_mono_class.h" #include "gd_mono_class.h"
#include "gd_mono_marshal.h" #include "gd_mono_marshal.h"
@ -397,10 +400,10 @@ MonoDomain *create_domain(const String &p_friendly_name) {
return domain; return domain;
} }
String get_exception_name_and_message(MonoObject *p_ex) { String get_exception_name_and_message(MonoException *p_ex) {
String res; String res;
MonoClass *klass = mono_object_get_class(p_ex); MonoClass *klass = mono_object_get_class((MonoObject *)p_ex);
MonoType *type = mono_class_get_type(klass); MonoType *type = mono_class_get_type(klass);
char *full_name = mono_type_full_name(type); char *full_name = mono_type_full_name(type);
@ -410,29 +413,31 @@ String get_exception_name_and_message(MonoObject *p_ex) {
res += ": "; res += ": ";
MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
MonoString *msg = (MonoString *)mono_property_get_value(prop, p_ex, NULL, NULL); MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL);
res += GDMonoMarshal::mono_string_to_godot(msg); res += GDMonoMarshal::mono_string_to_godot(msg);
return res; return res;
} }
void print_unhandled_exception(MonoObject *p_exc) { void debug_print_unhandled_exception(MonoException *p_exc) {
print_unhandled_exception(p_exc, false); print_unhandled_exception(p_exc);
debug_send_unhandled_exception_error(p_exc);
} }
void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { void debug_send_unhandled_exception_error(MonoException *p_exc) {
mono_print_unhandled_exception(p_exc);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (!ScriptDebugger::get_singleton()) if (!ScriptDebugger::get_singleton())
return; return;
_TLS_RECURSION_GUARD_;
ScriptLanguage::StackInfo separator; ScriptLanguage::StackInfo separator;
separator.file = ""; separator.file = String();
separator.func = "--- " + RTR("End of inner exception stack trace") + " ---"; separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
separator.line = 0; separator.line = 0;
Vector<ScriptLanguage::StackInfo> si; Vector<ScriptLanguage::StackInfo> si;
String exc_msg = ""; String exc_msg;
while (p_exc != NULL) { while (p_exc != NULL) {
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace); GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
@ -441,24 +446,16 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
MonoBoolean need_file_info = true; MonoBoolean need_file_info = true;
void *ctor_args[2] = { p_exc, &need_file_info }; void *ctor_args[2] = { p_exc, &need_file_info };
MonoObject *unexpected_exc = NULL; MonoException *unexpected_exc = NULL;
CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc); CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
if (unexpected_exc != NULL) { if (unexpected_exc) {
mono_print_unhandled_exception(unexpected_exc); GDMonoInternals::unhandled_exception(unexpected_exc);
_UNREACHABLE_();
if (p_recursion_caution) {
// 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; Vector<ScriptLanguage::StackInfo> _si;
if (stack_trace != NULL && !p_recursion_caution) { if (stack_trace != NULL) {
_si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace); _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
for (int i = _si.size() - 1; i >= 0; i--) for (int i = _si.size() - 1; i >= 0; i--)
si.insert(0, _si[i]); si.insert(0, _si[i]);
@ -466,10 +463,15 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc); exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException"); GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL; GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
if (p_exc != NULL) CRASH_COND(inner_exc_prop == NULL);
MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
if (inner_exc != NULL)
si.insert(0, separator); si.insert(0, separator);
p_exc = (MonoException *)inner_exc;
} }
String file = si.size() ? si[0].file : __FILE__; String file = si.size() ? si[0].file : __FILE__;
@ -481,4 +483,38 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
#endif #endif
} }
void debug_unhandled_exception(MonoException *p_exc) {
#ifdef DEBUG_ENABLED
GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
if (ScriptDebugger::get_singleton())
ScriptDebugger::get_singleton()->idle_poll();
#endif
GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
_UNREACHABLE_();
}
void print_unhandled_exception(MonoException *p_exc) {
mono_print_unhandled_exception((MonoObject *)p_exc);
}
void set_pending_exception(MonoException *p_exc) {
#ifdef HAS_PENDING_EXCEPTIONS
if (get_runtime_invoke_count() == 0) {
debug_unhandled_exception(p_exc);
_UNREACHABLE_();
}
if (!mono_runtime_set_pending_exception(p_exc, false)) {
ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:");
GDMonoUtils::debug_print_unhandled_exception(p_exc);
}
#else
debug_unhandled_exception(p_exc);
_UNREACHABLE_();
#endif
}
_THREAD_LOCAL_(int)
current_invoke_count = 0;
} // namespace GDMonoUtils } // namespace GDMonoUtils

View file

@ -34,6 +34,8 @@
#include <mono/metadata/threads.h> #include <mono/metadata/threads.h>
#include "../mono_gc_handle.h" #include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "../utils/thread_local.h"
#include "gd_mono_header.h" #include "gd_mono_header.h"
#include "object.h" #include "object.h"
@ -184,10 +186,28 @@ MonoObject *create_managed_from(const RID &p_from);
MonoDomain *create_domain(const String &p_friendly_name); MonoDomain *create_domain(const String &p_friendly_name);
String get_exception_name_and_message(MonoObject *p_ex); String get_exception_name_and_message(MonoException *p_ex);
void print_unhandled_exception(MonoObject *p_exc); void debug_print_unhandled_exception(MonoException *p_exc);
void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution); void debug_send_unhandled_exception_error(MonoException *p_exc);
_NO_RETURN_ void debug_unhandled_exception(MonoException *p_exc);
void print_unhandled_exception(MonoException *p_exc);
/**
* Sets the exception as pending. The exception will be thrown when returning to managed code.
* If no managed method is being invoked by the runtime, the exception will be treated as
* an unhandled exception and the method will not return.
*/
void set_pending_exception(MonoException *p_exc);
extern _THREAD_LOCAL_(int) current_invoke_count;
_FORCE_INLINE_ int get_runtime_invoke_count() {
return current_invoke_count;
}
_FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
return current_invoke_count;
}
} // namespace GDMonoUtils } // namespace GDMonoUtils
@ -206,4 +226,11 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution);
#define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) #define REAL_T_MONOCLASS CACHED_CLASS_RAW(float)
#endif #endif
#define GD_MONO_BEGIN_RUNTIME_INVOKE \
int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
_runtime_invoke_count_ref += 1;
#define GD_MONO_END_RUNTIME_INVOKE \
_runtime_invoke_count_ref -= 1;
#endif // GD_MONOUTILS_H #endif // GD_MONOUTILS_H

View file

@ -101,11 +101,13 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback);
MonoObject *ex = NULL; MonoException *exc = NULL;
thunk(get_target(), signal_args, &ex); GD_MONO_BEGIN_RUNTIME_INVOKE;
thunk(get_target(), signal_args, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(Variant()); ERR_FAIL_V(Variant());
} }
@ -133,11 +135,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() {
MonoObject *awaiter = get_target(); MonoObject *awaiter = get_target();
if (awaiter) { if (awaiter) {
MonoObject *ex = NULL; MonoException *exc = NULL;
thunk(awaiter, &ex); GD_MONO_BEGIN_RUNTIME_INVOKE;
thunk(awaiter, (MonoObject **)&exc);
GD_MONO_END_RUNTIME_INVOKE;
if (ex) { if (exc) {
mono_print_unhandled_exception(ex); GDMonoUtils::set_pending_exception(exc);
ERR_FAIL_V(); ERR_FAIL_V();
} }
} }

View file

@ -0,0 +1,36 @@
from __future__ import print_function
def supported(result):
return 'supported' if result else 'not supported'
def check_cxx11_thread_local(conf):
print('Checking for `thread_local` support...', end=" ")
result = conf.TryCompile('thread_local int foo = 0; int main() { return foo; }', '.cpp')
print(supported(result))
return bool(result)
def check_declspec_thread(conf):
print('Checking for `__declspec(thread)` support...', end=" ")
result = conf.TryCompile('__declspec(thread) int foo = 0; int main() { return foo; }', '.cpp')
print(supported(result))
return bool(result)
def check_gcc___thread(conf):
print('Checking for `__thread` support...', end=" ")
result = conf.TryCompile('__thread int foo = 0; int main() { return foo; }', '.cpp')
print(supported(result))
return bool(result)
def configure(conf):
if check_cxx11_thread_local(conf):
conf.env.Append(CPPDEFINES=['HAVE_CXX11_THREAD_LOCAL'])
else:
if conf.env.msvc:
if check_declspec_thread(conf):
conf.env.Append(CPPDEFINES=['HAVE_DECLSPEC_THREAD'])
elif check_gcc___thread(conf):
conf.env.Append(CPPDEFINES=['HAVE_GCC___THREAD'])

View file

@ -0,0 +1,59 @@
/*************************************************************************/
/* util_macros.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef UTIL_MACROS_H
#define UTIL_MACROS_H
// noreturn
#undef _NO_RETURN_
#ifdef __GNUC__
#define _NO_RETURN_ __attribute__((noreturn))
#elif _MSC_VER
#define _NO_RETURN_ __declspec(noreturn)
#else
#error Platform or compiler not supported
#endif
// unreachable
#if defined(_MSC_VER)
#define _UNREACHABLE_() __assume(0)
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
#define _UNREACHABLE_() __builtin_unreachable()
#else
#define _UNREACHABLE_() \
CRASH_NOW(); \
do { \
} while (true);
#endif
#endif // UTIL_MACROS_H

View file

@ -0,0 +1,99 @@
/*************************************************************************/
/* thread_local.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "thread_local.h"
#ifdef WINDOWS_ENABLED
#include <windows.h>
#else
#include <pthread.h>
#endif
#include "core/os/memory.h"
#include "core/print_string.h"
struct ThreadLocalStorage::Impl {
#ifdef WINDOWS_ENABLED
DWORD dwFlsIndex;
#else
pthread_key_t key;
#endif
void *get_value() const {
#ifdef WINDOWS_ENABLED
return FlsGetValue(dwFlsIndex);
#else
return pthread_getspecific(key);
#endif
}
void set_value(void *p_value) const {
#ifdef WINDOWS_ENABLED
FlsSetValue(dwFlsIndex, p_value);
#else
pthread_setspecific(key, p_value);
#endif
}
Impl(void (*p_destr_callback_func)(void *)) {
#ifdef WINDOWS_ENABLED
dwFlsIndex = FlsAlloc(p_destr_callback_func);
ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
#else
pthread_key_create(&key, p_destr_callback_func);
#endif
}
~Impl() {
#ifdef WINDOWS_ENABLED
FlsFree(dwFlsIndex);
#else
pthread_key_delete(key);
#endif
}
};
void *ThreadLocalStorage::get_value() const {
return pimpl->get_value();
}
void ThreadLocalStorage::set_value(void *p_value) const {
pimpl->set_value(p_value);
}
void ThreadLocalStorage::alloc(void (*p_destr_callback)(void *)) {
pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
}
void ThreadLocalStorage::free() {
memdelete(pimpl);
pimpl = NULL;
}

View file

@ -0,0 +1,171 @@
/*************************************************************************/
/* thread_local.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef THREAD_LOCAL_H
#define THREAD_LOCAL_H
#ifdef HAVE_CXX11_THREAD_LOCAL
#define _THREAD_LOCAL_(m_t) thread_local m_t
#else
#if !defined(__GNUC__) && !defined(_MSC_VER)
#error Platform or compiler not supported
#endif
#ifdef __GNUC__
#ifdef HAVE_GCC___THREAD
#define _THREAD_LOCAL_(m_t) __thread m_t
#else
#define USE_CUSTOM_THREAD_LOCAL
#endif
#elif _MSC_VER
#ifdef HAVE_DECLSPEC_THREAD
#define _THREAD_LOCAL_(m_t) __declspec(thread) m_t
#else
#define USE_CUSTOM_THREAD_LOCAL
#endif
#endif // __GNUC__ _MSC_VER
#endif // HAVE_CXX11_THREAD_LOCAL
#ifdef USE_CUSTOM_THREAD_LOCAL
#define _THREAD_LOCAL_(m_t) ThreadLocal<m_t>
#endif
#include "core/typedefs.h"
struct ThreadLocalStorage {
void *get_value() const;
void set_value(void *p_value) const;
void alloc(void (*p_dest_callback)(void *));
void free();
private:
struct Impl;
Impl *pimpl;
};
template <typename T>
class ThreadLocal {
ThreadLocalStorage storage;
T init_val;
#ifdef WINDOWS_ENABLED
#define _CALLBACK_FUNC_ __stdcall
#else
#define _CALLBACK_FUNC_
#endif
static void _CALLBACK_FUNC_ destr_callback(void *tls_data) {
memdelete(static_cast<T *>(tls_data));
}
#undef _CALLBACK_FUNC_
T *_tls_get_value() const {
void *tls_data = storage.get_value();
if (tls_data)
return static_cast<T *>(tls_data);
T *data = memnew(T(init_val));
storage.set_value(data);
return data;
}
public:
ThreadLocal() :
ThreadLocal(T()) {}
ThreadLocal(const T &p_init_val) :
init_val(p_init_val) {
storage.alloc(&destr_callback);
}
ThreadLocal(const ThreadLocal &other) :
ThreadLocal(*other._tls_get_value()) {}
~ThreadLocal() {
storage.free();
}
_FORCE_INLINE_ T *operator&() const {
return _tls_get_value();
}
_FORCE_INLINE_ operator T &() const {
return *_tls_get_value();
}
_FORCE_INLINE_ ThreadLocal &operator=(const T &val) {
T *ptr = _tls_get_value();
*ptr = val;
return *this;
}
};
struct FlagScopeGuard {
FlagScopeGuard(bool &p_flag) :
flag(p_flag) {
flag = !flag;
}
~FlagScopeGuard() {
flag = !flag;
}
private:
bool &flag;
};
#define _TLS_RECURSION_GUARD_V_(m_ret) \
static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
if (_recursion_flag_) \
return m_ret; \
FlagScopeGuard _recursion_guard_(_recursion_flag_);
#define _TLS_RECURSION_GUARD_ \
static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \
if (_recursion_flag_) \
return; \
FlagScopeGuard _recursion_guard_(_recursion_flag_);
#endif // THREAD_LOCAL_H