summaryrefslogtreecommitdiffstats
path: root/modules/mono
diff options
context:
space:
mode:
authorRaul Santos <raulsntos@gmail.com>2023-07-09 19:42:55 +0200
committerRaul Santos <raulsntos@gmail.com>2023-07-28 19:18:08 +0200
commit77e5e195f580c2d0bde25265653eb7f2461a7cdf (patch)
tree3abc0d6b1c4a084d102ae4f09351fd628f833bd0 /modules/mono
parent83cc5d4914a6bff76069ac19191192337e4df3de (diff)
downloadredot-engine-77e5e195f580c2d0bde25265653eb7f2461a7cdf.tar.gz
C#: Print error when MethodBind/Callable call fails
Diffstat (limited to 'modules/mono')
-rw-r--r--modules/mono/editor/bindings_generator.cpp17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs104
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs1
4 files changed, 123 insertions, 2 deletions
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 15bb574f4d..1e59b71bc3 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2087,6 +2087,11 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_expr_is_unsafe |= arg_type->cs_in_expr_is_unsafe;
}
+ // Collect caller name for MethodBind
+ if (p_imethod.is_vararg) {
+ icall_params += ", (godot_string_name)MethodName." + p_imethod.proxy_name + ".NativeValue";
+ }
+
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
@@ -2460,6 +2465,11 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
i++;
}
+ // Collect caller name for MethodBind
+ if (p_icall.is_vararg) {
+ c_func_sig << ", godot_string_name caller";
+ }
+
String icall_method = p_icall.name;
// Generate icall function
@@ -2525,7 +2535,12 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call("
<< CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
<< ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
- << ", total_length, out _);\n";
+ << ", total_length, out godot_variant_call_error vcall_error);\n";
+
+ r_output << base_indent << "ExceptionUtils.DebugCheckCallError(caller"
+ << ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
+ << ", total_length, vcall_error);\n";
if (!ret_void) {
if (return_type->cname != name_cache.type_Variant) {
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index 219a9a8c15..1239533a01 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -109,7 +109,8 @@ namespace Godot
}
godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
- (godot_variant**)argsPtr, argc, out _);
+ (godot_variant**)argsPtr, argc, out godot_variant_call_error vcall_error);
+ ExceptionUtils.DebugCheckCallError(callable, (godot_variant**)argsPtr, argc, vcall_error);
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
index 2d8067d300..ba2c232580 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -135,5 +135,109 @@ namespace Godot.NativeInterop
OnExceptionLoggerException(unexpected, e);
}
}
+
+ [Conditional("DEBUG")]
+ public unsafe static void DebugCheckCallError(godot_string_name method, IntPtr instance, godot_variant** args, int argCount, godot_variant_call_error error)
+ {
+ if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK)
+ {
+ using godot_variant instanceVariant = VariantUtils.CreateFromGodotObjectPtr(instance);
+ string where = GetCallErrorWhere(method, &instanceVariant, args, argCount);
+ string errorText = GetCallErrorMessage(error, where, args);
+ GD.PushError(errorText);
+ }
+ }
+
+ [Conditional("DEBUG")]
+ public unsafe static void DebugCheckCallError(in godot_callable callable, godot_variant** args, int argCount, godot_variant_call_error error)
+ {
+ if (error.Error != godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_OK)
+ {
+ using godot_variant callableVariant = VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(callable);
+ string where = $"callable '{VariantUtils.ConvertToString(callableVariant)}'";
+ string errorText = GetCallErrorMessage(error, where, args);
+ GD.PushError(errorText);
+ }
+ }
+
+ private unsafe static string GetCallErrorWhere(godot_string_name method, godot_variant* instance, godot_variant** args, int argCount)
+ {
+ string? methodstr = null;
+ string basestr = GetVariantTypeName(instance);
+
+ if (method == GodotObject.MethodName.Call || (basestr == "Godot.TreeItem" && method == TreeItem.MethodName.CallRecursive))
+ {
+ if (argCount >= 1)
+ {
+ methodstr = VariantUtils.ConvertToString(*args[0]);
+ }
+ }
+
+ if (string.IsNullOrEmpty(methodstr))
+ {
+ methodstr = StringName.CreateTakingOwnershipOfDisposableValue(method);
+ }
+
+ return $"function '{methodstr}' in base '{basestr}'";
+ }
+
+ private unsafe static string GetCallErrorMessage(godot_variant_call_error error, string where, godot_variant** args)
+ {
+ switch (error.Error)
+ {
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT:
+ {
+ int errorarg = error.Argument;
+ // Handle the Object to Object case separately as we don't have further class details.
+#if DEBUG
+ if (error.Expected == Variant.Type.Object && args[errorarg]->Type == error.Expected)
+ {
+ return $"Invalid type in {where}. The Object-derived class of argument {errorarg + 1} (" + GetVariantTypeName(args[errorarg]) + ") is not a subclass of the expected argument class.";
+ }
+ else if (error.Expected == Variant.Type.Array && args[errorarg]->Type == error.Expected)
+ {
+ return $"Invalid type in {where}. The array of argument {errorarg + 1} (" + GetVariantTypeName(args[errorarg]) + ") does not have the same element type as the expected typed array argument.";
+ }
+ else
+#endif
+ {
+ return $"Invalid type in {where}. Cannot convert argument {errorarg + 1} from {args[errorarg]->Type} to {error.Expected}.";
+ }
+ }
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS:
+ return $"Invalid call to {where}. Expected {error.Argument} arguments.";
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD:
+ return $"Invalid call. Nonexistent {where}.";
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL:
+ return $"Attempt to call {where} on a null instance.";
+ case godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_METHOD_NOT_CONST:
+ return $"Attempt to call {where} on a const instance.";
+ default:
+ return $"Bug, call error: #{error.Error}";
+ }
+ }
+
+ private unsafe static string GetVariantTypeName(godot_variant* variant)
+ {
+ if (variant->Type == Variant.Type.Object)
+ {
+ GodotObject obj = VariantUtils.ConvertToGodotObject(*variant);
+ if (obj == null)
+ {
+ return "null instance";
+ }
+ else if (!GodotObject.IsInstanceValid(obj))
+ {
+ return "previously freed";
+ }
+ else
+ {
+ return obj.GetType().ToString();
+ }
+ }
+
+ return variant->Type.ToString();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
index 43e7c7eb9a..1dddc82e85 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -71,6 +71,7 @@ namespace Godot.NativeInterop
GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL,
+ GODOT_CALL_ERROR_CALL_ERROR_METHOD_NOT_CONST,
}
[StructLayout(LayoutKind.Sequential)]