diff options
Diffstat (limited to 'modules')
46 files changed, 892 insertions, 503 deletions
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index d6abbc1bcf..7bab718b81 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -697,11 +697,21 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method); if (E) { godot_variant result; + +#ifdef DEBUG_ENABLED + current_method_call = p_method; +#endif + result = E->get().method.method((godot_object *)owner, E->get().method.method_data, userdata, p_argcount, (godot_variant **)p_args); + +#ifdef DEBUG_ENABLED + current_method_call = ""; +#endif + Variant res = *(Variant *)&result; godot_variant_destroy(&result); r_error.error = Variant::CallError::CALL_OK; @@ -716,6 +726,15 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p } void NativeScriptInstance::notification(int p_notification) { +#ifdef DEBUG_ENABLED + if (p_notification == MainLoop::NOTIFICATION_CRASH) { + if (current_method_call != StringName("")) { + ERR_PRINTS("NativeScriptInstance detected crash on method: " + current_method_call); + current_method_call = ""; + } + } +#endif + Variant value = p_notification; const Variant *args[1] = { &value }; call_multilevel("_notification", args, 1); diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index b47962dc37..be093dde4b 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -181,6 +181,9 @@ class NativeScriptInstance : public ScriptInstance { Object *owner; Ref<NativeScript> script; +#ifdef DEBUG_ENABLED + StringName current_method_call; +#endif void _ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 0e4724fec2..c0c3bd7b06 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -54,18 +54,18 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { } Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { - String _template = String() + - "extends %BASE%\n\n" + - "# class member variables go here, for example:\n" + - "# var a = 2\n" + - "# var b = \"textvar\"\n\n" + - "func _ready():\n" + - "%TS%# Called when the node is added to the scene for the first time.\n" + - "%TS%# Initialization here.\n" + - "%TS%pass\n\n" + - "#func _process(delta):\n" + - "#%TS%# Called every frame. Delta is time since last frame.\n" + - "#%TS%# Update game logic here.\n" + + String _template = "extends %BASE%\n" + "\n" + "# Declare member variables here. Examples:\n" + "# var a = 2\n" + "# var b = \"text\"\n" + "\n" + "# Called when the node enters the scene tree for the first time.\n" + "func _ready():\n" + "%TS%pass # Replace with function body.\n" + "\n" + "# Called every frame. 'delta' is the elapsed time since the previous frame.\n" + "#func _process(delta):\n" "#%TS%pass\n"; _template = _template.replace("%BASE%", p_base_class_name); diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 61130cb58f..10599f0c38 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1552,7 +1552,7 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret); if (gdfs && gdfs->function == function) { completed = false; - gdfs->previous_state = Ref<GDScriptFunctionState>(this); + gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this); } } @@ -1560,10 +1560,10 @@ Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_ar state.result = Variant(); if (completed) { - GDScriptFunctionState *state = this; - while (state != NULL) { - state->emit_signal("completed", ret); - state = *(state->previous_state); + if (first_state.is_valid()) { + first_state->emit_signal("completed", ret); + } else { + emit_signal("completed", ret); } } @@ -1614,7 +1614,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret); if (gdfs && gdfs->function == function) { completed = false; - gdfs->previous_state = Ref<GDScriptFunctionState>(this); + gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this); } } @@ -1622,10 +1622,10 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) { state.result = Variant(); if (completed) { - GDScriptFunctionState *state = this; - while (state != NULL) { - state->emit_signal("completed", ret); - state = *(state->previous_state); + if (first_state.is_valid()) { + first_state->emit_signal("completed", ret); + } else { + emit_signal("completed", ret); } } diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 836325f0fe..770d5c8733 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -234,7 +234,7 @@ class GDScriptFunctionState : public Reference { GDScriptFunction *function; GDScriptFunction::CallState state; Variant _signal_callback(const Variant **p_args, int p_argcount, Variant::CallError &r_error); - Ref<GDScriptFunctionState> previous_state; + Ref<GDScriptFunctionState> first_state; protected: static void _bind_methods(); diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index a88ba477c6..cccf09c58e 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -120,6 +120,7 @@ const char *GDScriptFunctions::get_func_name(Function p_func) { "Color8", "ColorN", "print_stack", + "get_stack", "instance_from_id", "len", "is_instance_valid", @@ -1213,6 +1214,22 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ }; } break; + case GET_STACK: { + VALIDATE_ARG_COUNT(0); + + ScriptLanguage *script = GDScriptLanguage::get_singleton(); + Array ret; + for (int i = 0; i < script->debug_get_stack_level_count(); i++) { + + Dictionary frame; + frame["source"] = script->debug_get_stack_level_source(i); + frame["function"] = script->debug_get_stack_level_function(i); + frame["line"] = script->debug_get_stack_level_line(i); + ret.push_back(frame); + }; + r_ret = ret; + } break; + case INSTANCE_FROM_ID: { VALIDATE_ARG_COUNT(1); @@ -1813,6 +1830,11 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) { mi.return_val.type = Variant::NIL; return mi; } break; + case GET_STACK: { + MethodInfo mi("get_stack"); + mi.return_val.type = Variant::NIL; + return mi; + } break; case INSTANCE_FROM_ID: { MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id")); diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h index c4731d17a4..899a7ebd25 100644 --- a/modules/gdscript/gdscript_functions.h +++ b/modules/gdscript/gdscript_functions.h @@ -111,6 +111,7 @@ public: COLOR8, COLORN, PRINT_STACK, + GET_STACK, INSTANCE_FROM_ID, LEN, IS_INSTANCE_VALID, diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index fdb92a68a9..9650563ee6 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -4093,7 +4093,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { #endif tokenizer->advance(); - Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport); + Node *subexpr = _parse_and_reduce_expression(p_class, false, autoexport || member._export.type != Variant::NIL); if (!subexpr) { if (_recover_from_completion()) { break; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 4b96824dca..fc5972c810 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -231,6 +231,13 @@ void GridMapEditor::_menu_option(int p_option) { _delete_selection(); } break; + case MENU_OPTION_SELECTION_FILL: { + if (!selection.active) + return; + + _fill_selection(); + + } break; case MENU_OPTION_GRIDMAP_SETTINGS: { settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50) * EDSCALE); } break; @@ -455,6 +462,29 @@ void GridMapEditor::_delete_selection() { _validate_selection(); } +void GridMapEditor::_fill_selection() { + + if (!selection.active) + return; + + undo_redo->create_action(TTR("GridMap Fill Selection")); + for (int i = selection.begin.x; i <= selection.end.x; i++) { + + for (int j = selection.begin.y; j <= selection.end.y; j++) { + + for (int k = selection.begin.z; k <= selection.end.z; k++) { + + undo_redo->add_do_method(node, "set_cell_item", i, j, k, selected_pallete, cursor_rot); + undo_redo->add_undo_method(node, "set_cell_item", i, j, k, node->get_cell_item(i, j, k), node->get_cell_item_orientation(i, j, k)); + } + } + } + undo_redo->commit_action(); + + selection.active = false; + _validate_selection(); +} + void GridMapEditor::_update_duplicate_indicator() { if (!selection.active || input_action != INPUT_DUPLICATE) { @@ -1072,6 +1102,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_SHIFT + KEY_C); options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_MASK_SHIFT + KEY_X); + options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_SHIFT + KEY_F); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Settings"), MENU_OPTION_GRIDMAP_SETTINGS); diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index f79d9aefa0..7c5feda125 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -168,6 +168,7 @@ class GridMapEditor : public VBoxContainer { MENU_OPTION_SELECTION_MAKE_EXTERIOR_CONNECTOR, MENU_OPTION_SELECTION_DUPLICATE, MENU_OPTION_SELECTION_CLEAR, + MENU_OPTION_SELECTION_FILL, MENU_OPTION_REMOVE_AREA, MENU_OPTION_GRIDMAP_SETTINGS @@ -200,6 +201,7 @@ class GridMapEditor : public VBoxContainer { void _floor_changed(float p_value); void _delete_selection(); + void _fill_selection(); EditorNode *editor; bool do_input_action(Camera *p_camera, const Point2 &p_point, bool p_click); diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 03e187e5b0..89a3caf4e4 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -5,9 +5,11 @@ Import('env_modules') 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): + from compat import byte_to_str + with open(dst, 'w') as header: header.write('/* This is an automatically generated file; DO NOT EDIT! OK THX */\n') header.write('#ifndef _CS_FILES_DATA_H\n') @@ -75,6 +77,13 @@ else: if ARGUMENTS.get('yolo_copy', False): 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 @@ -128,12 +137,13 @@ def find_msbuild_windows(): raise RuntimeError('Cannot find mono root directory') framework_path = os.path.join(mono_root, 'lib', 'mono', '4.5') - mono_bin_dir = os.path.join(mono_root, 'bin') - msbuild_mono = os.path.join(mono_bin_dir, 'msbuild.bat') 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 = { 'CscToolExe': os.path.join(mono_bin_dir, 'csc.bat'), 'VbcToolExe': os.path.join(mono_bin_dir, 'vbc.bat'), diff --git a/modules/mono/config.py b/modules/mono/config.py index ebf8512fb6..9a000a2a72 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -4,23 +4,15 @@ import os import sys import subprocess +from distutils.version import LooseVersion from SCons.Script import BoolVariable, Dir, Environment, File, PathVariable, SCons, Variables monoreg = imp.load_source('mono_reg_utils', 'modules/mono/mono_reg_utils.py') -def find_file_in_dir(directory, files, prefix='', extension=''): - if not extension.startswith('.'): - extension = '.' + extension - for curfile in files: - if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): - return curfile - return '' - - def can_build(env, platform): - if platform in ["javascript"]: + if platform in ['javascript']: return False # Not yet supported return True @@ -30,6 +22,27 @@ def is_enabled(): 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=''): + if not extension.startswith('.'): + extension = '.' + extension + for curfile in files: + if os.path.isfile(os.path.join(directory, prefix + curfile + extension)): + return curfile + return '' + + def copy_file(src_dir, dst_dir, name): from shutil import copyfile @@ -55,7 +68,7 @@ def custom_path_is_dir_create(key, val, env): def configure(env): env.use_ptrcall = True - env.add_module_version_string("mono") + env.add_module_version_string('mono') envvars = Variables() envvars.Add(BoolVariable('mono_static', 'Statically link mono', False)) @@ -84,6 +97,9 @@ def configure(env): if not mono_root: 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') env.Append(LIBPATH=mono_lib_path) @@ -149,20 +165,14 @@ def configure(env): # We can't use pkg-config to link mono statically, # but we can still use it to find the mono root directory if not mono_root and mono_static: - def pkgconfig_try_find_mono_root(): - 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() + mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext) if not mono_root: raise RuntimeError('Building with mono_static=yes, but failed to find the mono prefix with pkg-config. Specify one manually') 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') env.Append(LIBPATH=mono_lib_path) @@ -178,18 +188,18 @@ def configure(env): if mono_static: 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]) - 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']) else: raise RuntimeError('mono-static: Not supported on this platform') else: env.Append(LIBS=[mono_lib]) - if sys.platform == "darwin": + if sys.platform == 'darwin': 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']) if not mono_static: @@ -204,11 +214,14 @@ def configure(env): else: 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') mono_lib_path = '' 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.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH')) @@ -230,13 +243,44 @@ def configure(env): env.Append(LINKFLAGS='-rdynamic') -def get_doc_classes(): - return [ - "@C#", - "CSharpScript", - "GodotSharp", - ] +def configure_for_mono_version(env, mono_version): + if mono_version is None: + raise RuntimeError('Mono JIT compiler version not found') + print('Mono JIT compiler version: ' + str(mono_version)) + if mono_version >= LooseVersion("5.12.0"): + env.Append(CPPFLAGS=['-DHAS_PENDING_EXCEPTIONS']) -def get_doc_path(): - return "doc_classes" +def 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 '' + + +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): + from compat import decode_utf8 + + output = subprocess.check_output([os.path.join(mono_root, 'bin', 'mono'), '--version']) + first_line = decode_utf8(output.splitlines()[0]) + try: + return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())]) + except (ValueError, IndexError): + return None diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index fe8a3a8183..62a6b96bb5 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -49,6 +49,8 @@ #include "mono_gd/gd_mono_class.h" #include "mono_gd/gd_mono_marshal.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) @@ -296,30 +298,28 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin String script_template = "using " BINDINGS_NAMESPACE ";\n" "using System;\n" "\n" - "public class %CLASS_NAME% : %BASE_CLASS_NAME%\n" + "public class %CLASS% : %BASE%\n" "{\n" - " // Member variables here, example:\n" + " // Declare member variables here. Examples:\n" " // private int a = 2;\n" - " // private string b = \"textvar\";\n" + " // private string b = \"text\";\n" "\n" + " // Called when the node enters the scene tree for the first time.\n" " public override void _Ready()\n" " {\n" - " // Called every time the node is added to the scene.\n" - " // Initialization here.\n" " \n" " }\n" "\n" - "// public override void _Process(float delta)\n" - "// {\n" - "// // Called every frame. Delta is time since last frame.\n" - "// // Update game logic here.\n" - "// \n" - "// }\n" + "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n" + "// public override void _Process(float delta)\n" + "// {\n" + "// \n" + "// }\n" "}\n"; String base_class_name = get_base_class_name(p_base_class_name, p_class_name); - script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name) - .replace("%CLASS_NAME%", p_class_name); + script_template = script_template.replace("%BASE%", base_class_name) + .replace("%CLASS%", p_class_name); Ref<CSharpScript> script; script.instance(); @@ -476,7 +476,7 @@ String CSharpLanguage::_get_indentation() const { Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() { #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) return Vector<StackInfo>(); @@ -500,15 +500,15 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info() #ifdef DEBUG_ENABLED 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); - MonoArray *frames = st_get_frames(p_stack_trace, &exc); + MonoArray *frames = st_get_frames(p_stack_trace, (MonoObject **)&exc); if (exc) { - GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + GDMonoUtils::debug_print_unhandled_exception(exc); return Vector<StackInfo>(); } @@ -529,10 +529,10 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec MonoString *file_name; int file_line_num; 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) { - GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */); + GDMonoUtils::debug_print_unhandled_exception(exc); return Vector<StackInfo>(); } @@ -561,12 +561,12 @@ void CSharpLanguage::frame() { ERR_FAIL_NULL(thunk); - MonoObject *ex; - thunk(task_scheduler, &ex); + MonoException *exc = NULL; + thunk(task_scheduler, (MonoObject **)&exc); - if (ex) { - mono_print_unhandled_exception(ex); - ERR_FAIL(); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); + _UNREACHABLE_(); } } } @@ -1077,11 +1077,11 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const { GDMonoProperty *property = top->get_property(p_name); if (property) { - MonoObject *exc = NULL; + MonoException *exc = NULL; MonoObject *value = property->get_value(mono_object, &exc); if (exc) { r_ret = Variant(); - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } else { r_ret = GDMonoMarshal::mono_object_to_variant(value); } @@ -1489,12 +1489,12 @@ bool CSharpScript::_update_exports() { 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); - MonoObject *ex = NULL; - ctor->invoke(tmp_object, NULL, &ex); + MonoException *exc = NULL; + ctor->invoke(tmp_object, NULL, &exc); - if (ex) { + if (exc) { ERR_PRINT("Exception thrown from constructor of temporary MonoObject:"); - mono_print_unhandled_exception(ex); + GDMonoUtils::debug_print_unhandled_exception(exc); tmp_object = NULL; ERR_FAIL_V(false); } @@ -1543,11 +1543,11 @@ bool CSharpScript::_update_exports() { exported_members_cache.push_front(prop_info); if (tmp_object) { - MonoObject *exc = NULL; + MonoException *exc = NULL; MonoObject *ret = property->get_value(tmp_object, &exc); if (exc) { exported_members_defval_cache[name] = Variant(); - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); } else { exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret); } @@ -1915,7 +1915,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg // Construct 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 instance->gchandle = MonoGCHandle::create_strong(mono_object); diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp index e4269b0aec..bc95607743 100644 --- a/modules/mono/editor/csharp_project.cpp +++ b/modules/mono/editor/csharp_project.cpp @@ -47,11 +47,11 @@ String generate_core_api_project(const String &p_dir, const Vector<String> &p_fi Variant dir = p_dir; Variant compile_items = p_files; const Variant *args[2] = { &dir, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); 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 compile_items = p_files; const Variant *args[3] = { &dir, &core_dll_path, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); 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 compile_items = p_files; const Variant *args[3] = { &dir, &name, &compile_items }; - MonoObject *ex = NULL; - MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); 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 include = p_include; const Variant *args[3] = { &project_path, &item_type, &include }; - MonoObject *ex = NULL; - klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &ex); + MonoException *exc = NULL; + klass->get_method("AddItemToProjectChecked", 3)->invoke(NULL, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } } diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp index 43d58caad2..156a01ff5a 100644 --- a/modules/mono/editor/godotsharp_builds.cpp +++ b/modules/mono/editor/godotsharp_builds.cpp @@ -512,14 +512,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { const Variant *ctor_args[2] = { &solution, &config }; - MonoObject *ex = NULL; + MonoException *exc = NULL; 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; - GDMonoUtils::print_unhandled_exception(ex); - String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + GDMonoUtils::debug_unhandled_exception(exc); + String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); ERR_FAIL(); @@ -534,14 +534,14 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) { 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); - build_method->invoke(mono_object, args, &ex); + build_method->invoke(mono_object, args, &exc); - if (ex) { + if (exc) { exited = true; - GDMonoUtils::print_unhandled_exception(ex); - String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(ex); + GDMonoUtils::debug_unhandled_exception(exc); + String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc); build_tab->on_build_exec_failed(message); ERR_EXPLAIN(message); ERR_FAIL(); diff --git a/modules/mono/editor/monodevelop_instance.cpp b/modules/mono/editor/monodevelop_instance.cpp index 48a285561d..9f05711fd6 100644 --- a/modules/mono/editor/monodevelop_instance.cpp +++ b/modules/mono/editor/monodevelop_instance.cpp @@ -40,14 +40,14 @@ void MonoDevelopInstance::execute(const Vector<String> &p_files) { ERR_FAIL_NULL(execute_method); ERR_FAIL_COND(gc_handle.is_null()); - MonoObject *ex = NULL; + MonoException *exc = NULL; Variant files = p_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) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } } @@ -68,14 +68,14 @@ MonoDevelopInstance::MonoDevelopInstance(const String &p_solution) { MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr()); GDMonoMethod *ctor = klass->get_method(".ctor", 1); - MonoObject *ex = NULL; + MonoException *exc = NULL; Variant solution = p_solution; const Variant *args[1] = { &solution }; - ctor->invoke(obj, args, &ex); + ctor->invoke(obj, args, &exc); - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::debug_unhandled_exception(exc); ERR_FAIL(); } diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs index 0d20a12563..1ed2889833 100644 --- a/modules/mono/glue/cs_files/Mathf.cs +++ b/modules/mono/glue/cs_files/Mathf.cs @@ -150,7 +150,7 @@ namespace Godot public static real_t InverseLerp(real_t from, real_t to, real_t weight) { - return (Clamp(weight, 0f, 1f) - from) / (to - from); + return (weight - from) / (to - from); } public static bool IsInf(real_t s) @@ -165,7 +165,7 @@ namespace Godot public static real_t Lerp(real_t from, real_t to, real_t weight) { - return from + (to - from) * Clamp(weight, 0f, 1f); + return from + (to - from) * weight; } public static real_t Log(real_t s) diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp index 4e82bcd03e..12109045e0 100644 --- a/modules/mono/mono_gc_handle.cpp +++ b/modules/mono/mono_gc_handle.cpp @@ -34,15 +34,12 @@ uint32_t MonoGCHandle::make_strong_handle(MonoObject *p_object) { - return mono_gchandle_new( - p_object, - false /* do not pin the object */ - ); + return mono_gchandle_new(p_object, /* pinned: */ false); } 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) { diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 0646580eaa..bc84f43b4f 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -53,14 +53,6 @@ #include "main/main.h" #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 void gdmono_MonoPrintCallback(const char *string, mono_bool is_stdout) { @@ -197,6 +189,8 @@ void GDMono::initialize() { mono_config_parse(NULL); + mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL); + root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); 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"); #endif - mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL); - OS::get_singleton()->print("Mono: INITIALIZED\n"); } @@ -652,12 +644,12 @@ Error GDMono::_unload_scripts_domain() { _GodotSharp::get_singleton()->_dispose_callback(); - MonoObject *ex = NULL; - mono_domain_try_unload(domain, &ex); + MonoException *exc = NULL; + mono_domain_try_unload(domain, (MonoObject **)&exc); - if (ex) { - ERR_PRINT("Exception thrown when unloading scripts domain:"); - mono_print_unhandled_exception(ex); + if (exc) { + ERR_PRINT("Exception thrown when unloading scripts domain"); + GDMonoUtils::debug_unhandled_exception(exc); return FAILED; } @@ -763,12 +755,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); - MonoObject *ex = NULL; - mono_domain_try_unload(p_domain, &ex); + MonoException *exc = NULL; + mono_domain_try_unload(p_domain, (MonoObject **)&exc); - if (ex) { - ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`:"); - mono_print_unhandled_exception(ex); + if (exc) { + ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`"); + GDMonoUtils::debug_unhandled_exception(exc); return FAILED; } @@ -811,6 +803,21 @@ void GDMono::_domain_assemblies_cleanup(uint32_t 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() { singleton = this; diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 5e01152870..e0ec6ced5e 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -164,6 +164,8 @@ public: 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 void add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly); GDMonoAssembly **get_loaded_assembly(const String &p_name); diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index a1a79f957f..505c030ca1 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -32,8 +32,12 @@ #include "../csharp_script.h" #include "../mono_gc_handle.h" +#include "../utils/macros.h" +#include "../utils/thread_local.h" #include "gd_mono_utils.h" +#include <mono/metadata/exception.h> + namespace GDMonoInternals { void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { @@ -64,4 +68,11 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { return; } + +void unhandled_exception(MonoException *p_exc) { + mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well + abort(); + _UNREACHABLE_(); +} + } // namespace GDMonoInternals diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h index abec65e7d4..50cadcedf6 100644 --- a/modules/mono/mono_gd/gd_mono_internals.h +++ b/modules/mono/mono_gd/gd_mono_internals.h @@ -33,11 +33,20 @@ #include <mono/jit/jit.h> +#include "../utils/macros.h" + #include "core/object.h" namespace GDMonoInternals { 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 diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index c04fcca962..31c5bbb2fb 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -837,11 +837,13 @@ MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) { GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary); - MonoObject *ex = NULL; - MonoObject *ret = arrays_to_dict(keys, values, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(NULL); } @@ -858,11 +860,13 @@ Dictionary mono_object_to_Dictionary(MonoObject *p_dict) { MonoArray *keys = NULL; MonoArray *values = NULL; - MonoObject *ex = NULL; - dict_to_arrays(p_dict, &keys, &values, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(Dictionary()); } diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index ad52904945..c8df1038ce 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -95,7 +95,7 @@ void *GDMonoMethod::get_thunk() { 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) { 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); } - MonoObject *exc = NULL; - MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, &exc); + MonoException *exc = NULL; + 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) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } return ret; } else { - MonoObject *exc = NULL; - mono_runtime_invoke(mono_method, p_object, NULL, &exc); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { if (r_exc) { *r_exc = exc; } 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); return invoke_raw(p_object, NULL, r_exc); } -MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc) { - MonoObject *exc = NULL; - MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, &exc); +MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) { + MonoException *exc = NULL; + 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) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index a173af83f4..444ec2a67d 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -71,9 +71,9 @@ public: void *get_thunk(); - MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoObject **r_exc = NULL); - MonoObject *invoke(MonoObject *p_object, MonoObject **r_exc = NULL); - MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoObject **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = NULL); + MonoObject *invoke(MonoObject *p_object, MonoException **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_no_class() const; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 0fe527b199..1f837a2d78 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -138,47 +138,53 @@ bool GDMonoProperty::has_setter() { 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); MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); mono_array_set(params, MonoObject *, 0, p_value); - MonoObject *exc = NULL; - mono_runtime_invoke_array(prop_method, p_object, params, &exc); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; if (exc) { if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } } -void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoObject **r_exc) { - MonoObject *exc = NULL; - mono_property_set_value(mono_property, p_object, p_params, &exc); +void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { + MonoException *exc = NULL; + 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 (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } } -MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoObject **r_exc) { - MonoObject *exc = NULL; - MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, &exc); +MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) { + MonoException *exc = NULL; + 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) { ret = NULL; if (r_exc) { *r_exc = exc; } else { - GDMonoUtils::print_unhandled_exception(exc); + GDMonoUtils::set_pending_exception(exc); } } diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 2a0065e850..b3f1e2114a 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -62,9 +62,9 @@ public: _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, void **p_params, MonoObject **r_exc = NULL); - MonoObject *get_value(MonoObject *p_object, 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, MonoException **r_exc = NULL); + MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = NULL); bool get_bool_value(MonoObject *p_object); int get_int_value(MonoObject *p_object); diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index fbdbeaa3f7..a229552b76 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -30,12 +30,15 @@ #include "gd_mono_utils.h" +#include <mono/metadata/exception.h> + #include "os/dir_access.h" #include "os/os.h" #include "project_settings.h" #include "reference.h" #include "../csharp_script.h" +#include "../utils/macros.h" #include "gd_mono.h" #include "gd_mono_class.h" #include "gd_mono_marshal.h" @@ -397,10 +400,10 @@ MonoDomain *create_domain(const String &p_friendly_name) { return domain; } -String get_exception_name_and_message(MonoObject *p_ex) { +String get_exception_name_and_message(MonoException *p_ex) { 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); char *full_name = mono_type_full_name(type); @@ -410,29 +413,31 @@ String get_exception_name_and_message(MonoObject *p_ex) { res += ": "; 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); return res; } -void print_unhandled_exception(MonoObject *p_exc) { - print_unhandled_exception(p_exc, false); +void debug_print_unhandled_exception(MonoException *p_exc) { + print_unhandled_exception(p_exc); + debug_send_unhandled_exception_error(p_exc); } -void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { - mono_print_unhandled_exception(p_exc); +void debug_send_unhandled_exception_error(MonoException *p_exc) { #ifdef DEBUG_ENABLED if (!ScriptDebugger::get_singleton()) return; + _TLS_RECURSION_GUARD_; + ScriptLanguage::StackInfo separator; - separator.file = ""; + separator.file = String(); separator.func = "--- " + RTR("End of inner exception stack trace") + " ---"; separator.line = 0; Vector<ScriptLanguage::StackInfo> si; - String exc_msg = ""; + String exc_msg; while (p_exc != NULL) { 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; 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); - if (unexpected_exc != NULL) { - mono_print_unhandled_exception(unexpected_exc); - - 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(); - } + if (unexpected_exc) { + GDMonoInternals::unhandled_exception(unexpected_exc); + _UNREACHABLE_(); } 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); for (int i = _si.size() - 1; i >= 0; 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); - GDMonoProperty *p_prop = GDMono::get_singleton()->get_class(mono_object_get_class(p_exc))->get_property("InnerException"); - p_exc = p_prop != NULL ? p_prop->get_value(p_exc) : NULL; - if (p_exc != NULL) + GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class()); + GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException"); + 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); + + p_exc = (MonoException *)inner_exc; } String file = si.size() ? si[0].file : __FILE__; @@ -481,4 +483,38 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) { #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 diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index fc13a00e85..4f8e5932cd 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -34,6 +34,8 @@ #include <mono/metadata/threads.h> #include "../mono_gc_handle.h" +#include "../utils/macros.h" +#include "../utils/thread_local.h" #include "gd_mono_header.h" #include "object.h" @@ -184,10 +186,28 @@ MonoObject *create_managed_from(const RID &p_from); 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 print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution); +void debug_print_unhandled_exception(MonoException *p_exc); +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 @@ -206,4 +226,11 @@ void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution); #define REAL_T_MONOCLASS CACHED_CLASS_RAW(float) #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 diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp index b9d8520ac9..54720652fa 100644 --- a/modules/mono/signal_awaiter_utils.cpp +++ b/modules/mono/signal_awaiter_utils.cpp @@ -101,11 +101,13 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc GDMonoUtils::SignalAwaiter_SignalCallback thunk = CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback); - MonoObject *ex = NULL; - thunk(get_target(), signal_args, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + thunk(get_target(), signal_args, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(Variant()); } @@ -133,11 +135,13 @@ SignalAwaiterHandle::~SignalAwaiterHandle() { MonoObject *awaiter = get_target(); if (awaiter) { - MonoObject *ex = NULL; - thunk(awaiter, &ex); + MonoException *exc = NULL; + GD_MONO_BEGIN_RUNTIME_INVOKE; + thunk(awaiter, (MonoObject **)&exc); + GD_MONO_END_RUNTIME_INVOKE; - if (ex) { - mono_print_unhandled_exception(ex); + if (exc) { + GDMonoUtils::set_pending_exception(exc); ERR_FAIL_V(); } } diff --git a/modules/mono/tls_configure.py b/modules/mono/tls_configure.py new file mode 100644 index 0000000000..622280b00b --- /dev/null +++ b/modules/mono/tls_configure.py @@ -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']) diff --git a/modules/webm/resource_importer_webm.h b/modules/mono/utils/macros.h index d61e6e2a93..337a86870e 100644 --- a/modules/webm/resource_importer_webm.h +++ b/modules/mono/utils/macros.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* resource_importer_webm.h */ +/* util_macros.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,29 +28,32 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RESOURCEIMPORTERWEBM_H -#define RESOURCEIMPORTERWEBM_H +#ifndef UTIL_MACROS_H +#define UTIL_MACROS_H -#include "io/resource_import.h" +// noreturn -class ResourceImporterWebm : public ResourceImporter { - GDCLASS(ResourceImporterWebm, ResourceImporter) -public: - virtual String get_importer_name() const; - virtual String get_visible_name() const; - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual String get_save_extension() const; - virtual String get_resource_type() const; +#undef _NO_RETURN_ - virtual int get_preset_count() const; - virtual String get_preset_name(int p_idx) const; +#ifdef __GNUC__ +#define _NO_RETURN_ __attribute__((noreturn)) +#elif _MSC_VER +#define _NO_RETURN_ __declspec(noreturn) +#else +#error Platform or compiler not supported +#endif - virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; - virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; +// unreachable - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); +#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 - ResourceImporterWebm(); -}; - -#endif // RESOURCEIMPORTERWEBM_H +#endif // UTIL_MACROS_H diff --git a/modules/theora/resource_importer_theora.h b/modules/mono/utils/thread_local.cpp index e3c79287ad..6f8b0f90bc 100644 --- a/modules/theora/resource_importer_theora.h +++ b/modules/mono/utils/thread_local.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* resource_importer_theora.h */ +/* thread_local.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,31 +28,72 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RESOURCEIMPORTEROGGTHEORA_H -#define RESOURCEIMPORTEROGGTHEORA_H +#include "thread_local.h" -#include "video_stream_theora.h" +#ifdef WINDOWS_ENABLED +#include <windows.h> +#else +#include <pthread.h> +#endif -#include "core/io/resource_import.h" +#include "core/os/memory.h" +#include "core/print_string.h" -class ResourceImporterTheora : public ResourceImporter { - GDCLASS(ResourceImporterTheora, ResourceImporter) -public: - virtual String get_importer_name() const; - virtual String get_visible_name() const; - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual String get_save_extension() const; - virtual String get_resource_type() const; +struct ThreadLocalStorage::Impl { - virtual int get_preset_count() const; - virtual String get_preset_name(int p_idx) const; +#ifdef WINDOWS_ENABLED + DWORD dwFlsIndex; +#else + pthread_key_t key; +#endif - virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; - virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + void *get_value() const { +#ifdef WINDOWS_ENABLED + return FlsGetValue(dwFlsIndex); +#else + return pthread_getspecific(key); +#endif + } - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); + void set_value(void *p_value) const { +#ifdef WINDOWS_ENABLED + FlsSetValue(dwFlsIndex, p_value); +#else + pthread_setspecific(key, p_value); +#endif + } - ResourceImporterTheora(); + 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 + } }; -#endif // RESOURCEIMPORTEROGGTHEORA_H +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; +} diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h new file mode 100644 index 0000000000..7ff10b4efc --- /dev/null +++ b/modules/mono/utils/thread_local.h @@ -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 diff --git a/modules/regex/SCsub b/modules/regex/SCsub index 18b4051afe..4b8d5e9283 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -19,10 +19,13 @@ if env['builtin_pcre2']: "pcre2_compile.c", "pcre2_config.c", "pcre2_context.c", + "pcre2_convert.c", "pcre2_dfa_match.c", "pcre2_error.c", + "pcre2_extuni.c", "pcre2_find_bracket.c", "pcre2_jit_compile.c", + #"pcre2_jit_match.c", "pcre2_jit_misc.c", # these files are included in pcre2_jit_compile.c. "pcre2_maketables.c", "pcre2_match.c", "pcre2_match_data.c", diff --git a/modules/theora/doc_classes/ResourceImporterTheora.xml b/modules/theora/doc_classes/ResourceImporterTheora.xml deleted file mode 100644 index 5fc40a6eba..0000000000 --- a/modules/theora/doc_classes/ResourceImporterTheora.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterTheora" inherits="ResourceImporter" category="Core" version="3.1"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> - <demos> - </demos> - <methods> - </methods> - <constants> - </constants> -</class> diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp index 9bc5ed903a..971fe39c44 100644 --- a/modules/theora/register_types.cpp +++ b/modules/theora/register_types.cpp @@ -29,18 +29,22 @@ /*************************************************************************/ #include "register_types.h" -#include "resource_importer_theora.h" + #include "video_stream_theora.h" +static ResourceFormatLoaderTheora *resource_loader_theora = NULL; + void register_theora_types() { -#ifdef TOOLS_ENABLED - Ref<ResourceImporterTheora> theora_import; - theora_import.instance(); - ResourceFormatImporter::get_singleton()->add_importer(theora_import); -#endif + resource_loader_theora = memnew(ResourceFormatLoaderTheora); + ResourceLoader::add_resource_format_loader(resource_loader_theora, true); + ClassDB::register_class<VideoStreamTheora>(); } void unregister_theora_types() { + + if (resource_loader_theora) { + memdelete(resource_loader_theora); + } } diff --git a/modules/theora/resource_importer_theora.cpp b/modules/theora/resource_importer_theora.cpp deleted file mode 100644 index ee9bab74a7..0000000000 --- a/modules/theora/resource_importer_theora.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/*************************************************************************/ -/* resource_importer_theora.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 "resource_importer_theora.h" - -#include "io/resource_saver.h" -#include "os/file_access.h" -#include "scene/resources/texture.h" - -String ResourceImporterTheora::get_importer_name() const { - - return "Theora"; -} - -String ResourceImporterTheora::get_visible_name() const { - - return "Theora"; -} -void ResourceImporterTheora::get_recognized_extensions(List<String> *p_extensions) const { - - p_extensions->push_back("ogv"); - p_extensions->push_back("ogm"); -} - -String ResourceImporterTheora::get_save_extension() const { - return "ogvstr"; -} - -String ResourceImporterTheora::get_resource_type() const { - - return "VideoStreamTheora"; -} - -bool ResourceImporterTheora::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - - return true; -} - -int ResourceImporterTheora::get_preset_count() const { - return 0; -} -String ResourceImporterTheora::get_preset_name(int p_idx) const { - - return String(); -} - -void ResourceImporterTheora::get_import_options(List<ImportOption> *r_options, int p_preset) const { - - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); -} - -Error ResourceImporterTheora::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { - - VideoStreamTheora *stream = memnew(VideoStreamTheora); - stream->set_file(p_source_file); - - Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream); - - return ResourceSaver::save(p_save_path + ".ogvstr", ogv_stream); -} - -ResourceImporterTheora::ResourceImporterTheora() { -} diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 9e6307c0bf..881808873b 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -730,3 +730,46 @@ void VideoStreamTheora::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); } + +//////////// + +RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + memdelete(f); + return RES(); + } + + VideoStreamTheora *stream = memnew(VideoStreamTheora); + stream->set_file(p_path); + + Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream); + + if (r_error) { + *r_error = OK; + } + + return ogv_stream; +} + +void ResourceFormatLoaderTheora::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("ogv"); +} + +bool ResourceFormatLoaderTheora::handles_type(const String &p_type) const { + + return ClassDB::is_parent_class(p_type, "VideoStream"); +} + +String ResourceFormatLoaderTheora::get_resource_type(const String &p_path) const { + + String el = p_path.get_extension().to_lower(); + if (el == "ogv") + return "VideoStreamTheora"; + return ""; +} diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 4bdbbdae20..7cee1b491b 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -163,7 +163,7 @@ public: class VideoStreamTheora : public VideoStream { GDCLASS(VideoStreamTheora, VideoStream); - RES_BASE_EXTENSION("ogvstr"); + RES_BASE_EXTENSION("ogv"); String file; int audio_track; @@ -186,4 +186,12 @@ public: VideoStreamTheora() { audio_track = 0; } }; +class ResourceFormatLoaderTheora : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + #endif diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 72b5e09222..0bd64d6a1d 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -137,7 +137,7 @@ class VisualScriptEditor : public ScriptEditorBase { Vector<Pair<Variant::Type, String> > args; }; - HashMap<StringName, Ref<StyleBox>, StringNameHasher> node_styles; + HashMap<StringName, Ref<StyleBox> > node_styles; StringName edited_func; void _update_graph_connections(); diff --git a/modules/webm/doc_classes/ResourceImporterWebm.xml b/modules/webm/doc_classes/ResourceImporterWebm.xml deleted file mode 100644 index 0cfab1baf0..0000000000 --- a/modules/webm/doc_classes/ResourceImporterWebm.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="ResourceImporterWebm" inherits="ResourceImporter" category="Core" version="3.1"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> - <demos> - </demos> - <methods> - </methods> - <constants> - </constants> -</class> diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp index 1183dd41f7..121b528d5b 100644 --- a/modules/webm/register_types.cpp +++ b/modules/webm/register_types.cpp @@ -29,18 +29,22 @@ /*************************************************************************/ #include "register_types.h" -#include "resource_importer_webm.h" + #include "video_stream_webm.h" +static ResourceFormatLoaderWebm *resource_loader_webm = NULL; + void register_webm_types() { -#ifdef TOOLS_ENABLED - Ref<ResourceImporterWebm> webm_import; - webm_import.instance(); - ResourceFormatImporter::get_singleton()->add_importer(webm_import); -#endif + resource_loader_webm = memnew(ResourceFormatLoaderWebm); + ResourceLoader::add_resource_format_loader(resource_loader_webm, true); + ClassDB::register_class<VideoStreamWebm>(); } void unregister_webm_types() { + + if (resource_loader_webm) { + memdelete(resource_loader_webm); + } } diff --git a/modules/webm/resource_importer_webm.cpp b/modules/webm/resource_importer_webm.cpp deleted file mode 100644 index 7124a503e8..0000000000 --- a/modules/webm/resource_importer_webm.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************/ -/* resource_importer_webm.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 "resource_importer_webm.h" - -#include "io/resource_saver.h" -#include "os/file_access.h" -#include "scene/resources/texture.h" -#include "video_stream_webm.h" - -String ResourceImporterWebm::get_importer_name() const { - - return "Webm"; -} - -String ResourceImporterWebm::get_visible_name() const { - - return "Webm"; -} -void ResourceImporterWebm::get_recognized_extensions(List<String> *p_extensions) const { - - p_extensions->push_back("webm"); -} - -String ResourceImporterWebm::get_save_extension() const { - return "webmstr"; -} - -String ResourceImporterWebm::get_resource_type() const { - - return "VideoStreamWebm"; -} - -bool ResourceImporterWebm::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - - return true; -} - -int ResourceImporterWebm::get_preset_count() const { - return 0; -} -String ResourceImporterWebm::get_preset_name(int p_idx) const { - - return String(); -} - -void ResourceImporterWebm::get_import_options(List<ImportOption> *r_options, int p_preset) const { - - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); -} - -Error ResourceImporterWebm::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { - - FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); - if (!f) { - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); - } - memdelete(f); - - VideoStreamWebm *stream = memnew(VideoStreamWebm); - stream->set_file(p_source_file); - - Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream); - - return ResourceSaver::save(p_save_path + ".webmstr", webm_stream); -} - -ResourceImporterWebm::ResourceImporterWebm() { -} diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index fac47225bc..1bb9a43886 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -443,3 +443,46 @@ void VideoStreamWebm::set_audio_track(int p_track) { audio_track = p_track; } + +//////////// + +RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + memdelete(f); + return RES(); + } + + VideoStreamWebm *stream = memnew(VideoStreamWebm); + stream->set_file(p_path); + + Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream); + + if (r_error) { + *r_error = OK; + } + + return webm_stream; +} + +void ResourceFormatLoaderWebm::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("webm"); +} + +bool ResourceFormatLoaderWebm::handles_type(const String &p_type) const { + + return ClassDB::is_parent_class(p_type, "VideoStream"); +} + +String ResourceFormatLoaderWebm::get_resource_type(const String &p_path) const { + + String el = p_path.get_extension().to_lower(); + if (el == "webm") + return "VideoStreamWebm"; + return ""; +} diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index dde993d154..08be50846d 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -109,7 +109,7 @@ private: class VideoStreamWebm : public VideoStream { GDCLASS(VideoStreamWebm, VideoStream); - RES_BASE_EXTENSION("webmstr"); + RES_BASE_EXTENSION("webm"); String file; int audio_track; @@ -127,4 +127,12 @@ public: virtual void set_audio_track(int p_track); }; +class ResourceFormatLoaderWebm : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + #endif // VIDEO_STREAM_WEBM_H |