Fix Callable call error reporting.

* Fix potential crash when using bind in `Variant::get_callable_error_text()`
* Properly compute bound arguments so they can be properly shown.
* Add a function to obtain the actual bound arguments.
This commit is contained in:
Juan Linietsky 2023-01-10 13:08:10 +01:00
parent 91713ced81
commit 33d3b7eea7
8 changed files with 108 additions and 2 deletions

View file

@ -1055,7 +1055,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
//most likely object is not initialized yet, do not throw error.
} else {
ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc + c.callable.get_bound_arguments_count(), ce) + ".");
ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
err = ERR_METHOD_NOT_FOUND;
}
}

View file

@ -117,6 +117,7 @@ Callable Callable::bindv(const Array &p_arguments) {
}
Callable Callable::unbind(int p_argcount) const {
ERR_FAIL_COND_V_MSG(p_argcount <= 0, Callable(*this), "Amount of unbind() arguments must be 1 or greater.");
return Callable(memnew(CallableCustomUnbind(*this, p_argcount)));
}
@ -159,6 +160,27 @@ int Callable::get_bound_arguments_count() const {
}
}
void Callable::get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const {
if (!is_null() && is_custom()) {
custom->get_bound_arguments(r_arguments, r_argcount);
} else {
r_arguments.clear();
r_argcount = 0;
}
}
Array Callable::get_bound_arguments() const {
Vector<Variant> arr;
int ac;
get_bound_arguments_ref(arr, ac);
Array ret;
ret.resize(arr.size());
for (int i = 0; i < arr.size(); i++) {
ret[i] = arr[i];
}
return ret;
}
CallableCustom *Callable::get_custom() const {
ERR_FAIL_COND_V_MSG(!is_custom(), nullptr,
vformat("Can't get custom on non-CallableCustom \"%s\".", operator String()));
@ -370,6 +392,11 @@ int CallableCustom::get_bound_arguments_count() const {
return 0;
}
void CallableCustom::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
r_arguments = Vector<Variant>();
r_argcount = 0;
}
CallableCustom::CallableCustom() {
ref_count.init();
}

View file

@ -108,6 +108,8 @@ public:
StringName get_method() const;
CallableCustom *get_custom() const;
int get_bound_arguments_count() const;
void get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const; // Internal engine use, the exposed one is below.
Array get_bound_arguments() const;
uint32_t hash() const;
@ -149,6 +151,7 @@ public:
virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const;
virtual const Callable *get_base_comparator() const;
virtual int get_bound_arguments_count() const;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const;
CallableCustom();
virtual ~CallableCustom() {}

View file

@ -91,6 +91,43 @@ int CallableCustomBind::get_bound_arguments_count() const {
return callable.get_bound_arguments_count() + binds.size();
}
void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
Vector<Variant> sub_args;
int sub_count;
callable.get_bound_arguments_ref(sub_args, sub_count);
if (sub_count == 0) {
r_arguments = binds;
r_argcount = binds.size();
return;
}
int new_count = sub_count + binds.size();
r_argcount = new_count;
if (new_count <= 0) {
// Removed more arguments than it adds.
r_arguments = Vector<Variant>();
return;
}
r_arguments.resize(new_count);
if (sub_count > 0) {
for (int i = 0; i < sub_count; i++) {
r_arguments.write[i] = sub_args[i];
}
for (int i = 0; i < binds.size(); i++) {
r_arguments.write[i + sub_count] = binds[i];
}
r_argcount = new_count;
} else {
for (int i = 0; i < binds.size() + sub_count; i++) {
r_arguments.write[i] = binds[i - sub_count];
}
}
}
void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount));
for (int i = 0; i < p_argcount; i++) {
@ -172,6 +209,21 @@ int CallableCustomUnbind::get_bound_arguments_count() const {
return callable.get_bound_arguments_count() - argcount;
}
void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
Vector<Variant> sub_args;
int sub_count;
callable.get_bound_arguments_ref(sub_args, sub_count);
r_argcount = sub_args.size() - argcount;
if (argcount >= sub_args.size()) {
r_arguments = Vector<Variant>();
} else {
sub_args.resize(sub_args.size() - argcount);
r_arguments = sub_args;
}
}
void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
if (argcount > p_argcount) {
r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;

View file

@ -52,6 +52,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
Callable get_callable() { return callable; }
Vector<Variant> get_binds() { return binds; }
@ -77,6 +78,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
Callable get_callable() { return callable; }
int get_unbinds() { return argcount; }

View file

@ -3654,7 +3654,22 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
}
String Variant::get_callable_error_text(const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) {
return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, p_argcount, ce);
Vector<Variant> binds;
int args_bound;
p_callable.get_bound_arguments_ref(binds, args_bound);
if (args_bound <= 0) {
return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, MAX(0, p_argcount + args_bound), ce);
} else {
Vector<const Variant *> argptrs;
argptrs.resize(p_argcount + binds.size());
for (int i = 0; i < p_argcount; i++) {
argptrs.write[i] = p_argptrs[i];
}
for (int i = 0; i < binds.size(); i++) {
argptrs.write[i + p_argcount] = &binds[i];
}
return get_call_error_text(p_callable.get_object(), p_callable.get_method(), (const Variant **)argptrs.ptr(), argptrs.size(), ce);
}
}
void Variant::register_types() {

View file

@ -2017,6 +2017,7 @@ static void _register_variant_builtin_methods() {
bind_method(Callable, get_object_id, sarray(), varray());
bind_method(Callable, get_method, sarray(), varray());
bind_method(Callable, get_bound_arguments_count, sarray(), varray());
bind_method(Callable, get_bound_arguments, sarray(), varray());
bind_method(Callable, hash, sarray(), varray());
bind_method(Callable, bindv, sarray("arguments"), varray());
bind_method(Callable, unbind, sarray("argcount"), varray());

View file

@ -107,6 +107,12 @@
Calls the method represented by this [Callable]. Unlike [method call], this method expects all arguments to be contained inside the [param arguments] [Array].
</description>
</method>
<method name="get_bound_arguments" qualifiers="const">
<return type="Array" />
<description>
Return the bound arguments (as long as [method get_bound_arguments_count] is greater than zero), or empty (if [method get_bound_arguments_count] is less than or equal to zero).
</description>
</method>
<method name="get_bound_arguments_count" qualifiers="const">
<return type="int" />
<description>