summaryrefslogtreecommitdiff
path: root/core/variant
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2023-01-10 13:08:10 +0100
committerJuan Linietsky <reduzio@gmail.com>2023-01-10 13:56:27 +0100
commit33d3b7eea77517cd2fcce59c5e9128aa74511f9f (patch)
tree7e6eebb865de9b2a38589379644e557bf448577f /core/variant
parent91713ced81792b10fdc9367b7f355738e5d52777 (diff)
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.
Diffstat (limited to 'core/variant')
-rw-r--r--core/variant/callable.cpp27
-rw-r--r--core/variant/callable.h3
-rw-r--r--core/variant/callable_bind.cpp52
-rw-r--r--core/variant/callable_bind.h2
-rw-r--r--core/variant/variant.cpp17
-rw-r--r--core/variant/variant_call.cpp1
6 files changed, 101 insertions, 1 deletions
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index fd85fe78f6..2f2acc55a6 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -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();
}
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 10d291ac24..0abbb64c0b 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -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() {}
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index 83035dc70d..5be91c6e11 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -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;
diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h
index a79a521b5b..278ed335d0 100644
--- a/core/variant/callable_bind.h
+++ b/core/variant/callable_bind.h
@@ -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; }
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index bff6656a88..ca42738b05 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -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() {
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 6c06b63dab..05fb62ff12 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -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());