Skip to content

Commit

Permalink
Fix Callable call error reporting.
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
reduz authored and Streq committed Feb 9, 2023
1 parent 01c92ba commit 7baa374
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 2 deletions.
2 changes: 1 addition & 1 deletion core/object/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
27 changes: 27 additions & 0 deletions core/variant/callable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
}

Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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();
}
Expand Down
3 changes: 3 additions & 0 deletions core/variant/callable.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class Callable {
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;

Expand Down Expand Up @@ -149,6 +151,7 @@ class CallableCustom {
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() {}
Expand Down
52 changes: 52 additions & 0 deletions core/variant/callable_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions core/variant/callable_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class CallableCustomBind : public CallableCustom {
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; }

Expand All @@ -77,6 +78,7 @@ class CallableCustomUnbind : public CallableCustom {
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; }
Expand Down
17 changes: 16 additions & 1 deletion core/variant/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions core/variant/variant_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
6 changes: 6 additions & 0 deletions doc/classes/Callable.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

0 comments on commit 7baa374

Please sign in to comment.