Skip to content

Commit e379cc7

Browse files
committedNov 4, 2024
Core: Fix Callable.get_bound_arguments{,_count}() return incorrect data
1 parent 1bffd6c commit e379cc7

File tree

9 files changed

+150
-63
lines changed

9 files changed

+150
-63
lines changed
 

‎core/variant/callable.cpp

+17-8
Original file line numberDiff line numberDiff line change
@@ -206,19 +206,17 @@ int Callable::get_bound_arguments_count() const {
206206
}
207207
}
208208

209-
void Callable::get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const {
209+
void Callable::get_bound_arguments_ref(Vector<Variant> &r_arguments) const {
210210
if (!is_null() && is_custom()) {
211-
custom->get_bound_arguments(r_arguments, r_argcount);
211+
custom->get_bound_arguments(r_arguments);
212212
} else {
213213
r_arguments.clear();
214-
r_argcount = 0;
215214
}
216215
}
217216

218217
Array Callable::get_bound_arguments() const {
219218
Vector<Variant> arr;
220-
int ac;
221-
get_bound_arguments_ref(arr, ac);
219+
get_bound_arguments_ref(arr);
222220
Array ret;
223221
ret.resize(arr.size());
224222
for (int i = 0; i < arr.size(); i++) {
@@ -227,6 +225,14 @@ Array Callable::get_bound_arguments() const {
227225
return ret;
228226
}
229227

228+
int Callable::get_unbound_arguments_count() const {
229+
if (!is_null() && is_custom()) {
230+
return custom->get_unbound_arguments_count();
231+
} else {
232+
return 0;
233+
}
234+
}
235+
230236
CallableCustom *Callable::get_custom() const {
231237
ERR_FAIL_COND_V_MSG(!is_custom(), nullptr,
232238
vformat("Can't get custom on non-CallableCustom \"%s\".", operator String()));
@@ -464,9 +470,12 @@ int CallableCustom::get_bound_arguments_count() const {
464470
return 0;
465471
}
466472

467-
void CallableCustom::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
468-
r_arguments = Vector<Variant>();
469-
r_argcount = 0;
473+
void CallableCustom::get_bound_arguments(Vector<Variant> &r_arguments) const {
474+
r_arguments.clear();
475+
}
476+
477+
int CallableCustom::get_unbound_arguments_count() const {
478+
return 0;
470479
}
471480

472481
CallableCustom::CallableCustom() {

‎core/variant/callable.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ class Callable {
111111
CallableCustom *get_custom() const;
112112
int get_argument_count(bool *r_is_valid = nullptr) const;
113113
int get_bound_arguments_count() const;
114-
void get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const; // Internal engine use, the exposed one is below.
114+
void get_bound_arguments_ref(Vector<Variant> &r_arguments) const; // Internal engine use, the exposed one is below.
115115
Array get_bound_arguments() const;
116+
int get_unbound_arguments_count() const;
116117

117118
uint32_t hash() const;
118119

@@ -158,7 +159,8 @@ class CallableCustom {
158159
virtual const Callable *get_base_comparator() const;
159160
virtual int get_argument_count(bool &r_is_valid) const;
160161
virtual int get_bound_arguments_count() const;
161-
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const;
162+
virtual void get_bound_arguments(Vector<Variant> &r_arguments) const;
163+
virtual int get_unbound_arguments_count() const;
162164

163165
CallableCustom();
164166
virtual ~CallableCustom() {}

‎core/variant/callable_bind.cpp

+29-38
Original file line numberDiff line numberDiff line change
@@ -100,44 +100,42 @@ int CallableCustomBind::get_argument_count(bool &r_is_valid) const {
100100
}
101101

102102
int CallableCustomBind::get_bound_arguments_count() const {
103-
return callable.get_bound_arguments_count() + binds.size();
103+
return callable.get_bound_arguments_count() + MAX(0, binds.size() - callable.get_unbound_arguments_count());
104104
}
105105

106-
void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
107-
Vector<Variant> sub_args;
108-
int sub_count;
109-
callable.get_bound_arguments_ref(sub_args, sub_count);
106+
void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments) const {
107+
Vector<Variant> sub_bound_args;
108+
callable.get_bound_arguments_ref(sub_bound_args);
109+
int sub_bound_count = sub_bound_args.size();
110110

111-
if (sub_count == 0) {
111+
int sub_unbound_count = callable.get_unbound_arguments_count();
112+
113+
if (sub_bound_count == 0 && sub_unbound_count == 0) {
112114
r_arguments = binds;
113-
r_argcount = binds.size();
114115
return;
115116
}
116117

117-
int new_count = sub_count + binds.size();
118-
r_argcount = new_count;
118+
int added_count = MAX(0, binds.size() - sub_unbound_count);
119+
int new_count = sub_bound_count + added_count;
119120

120-
if (new_count <= 0) {
121-
// Removed more arguments than it adds.
122-
r_arguments = Vector<Variant>();
121+
if (added_count <= 0) {
122+
// All added arguments are consumed by `sub_unbound_count`.
123+
r_arguments = sub_bound_args;
123124
return;
124125
}
125126

126127
r_arguments.resize(new_count);
127-
128-
if (sub_count > 0) {
129-
for (int i = 0; i < sub_count; i++) {
130-
r_arguments.write[i] = sub_args[i];
131-
}
132-
for (int i = 0; i < binds.size(); i++) {
133-
r_arguments.write[i + sub_count] = binds[i];
134-
}
135-
r_argcount = new_count;
136-
} else {
137-
for (int i = 0; i < binds.size() + sub_count; i++) {
138-
r_arguments.write[i] = binds[i - sub_count];
139-
}
128+
Variant *args = r_arguments.ptrw();
129+
for (int i = 0; i < added_count; i++) {
130+
args[i] = binds[i];
140131
}
132+
for (int i = 0; i < sub_bound_count; i++) {
133+
args[i + added_count] = sub_bound_args[i];
134+
}
135+
}
136+
137+
int CallableCustomBind::get_unbound_arguments_count() const {
138+
return MAX(0, callable.get_unbound_arguments_count() - binds.size());
141139
}
142140

143141
void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
@@ -242,22 +240,15 @@ int CallableCustomUnbind::get_argument_count(bool &r_is_valid) const {
242240
}
243241

244242
int CallableCustomUnbind::get_bound_arguments_count() const {
245-
return callable.get_bound_arguments_count() - argcount;
243+
return callable.get_bound_arguments_count();
246244
}
247245

248-
void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const {
249-
Vector<Variant> sub_args;
250-
int sub_count;
251-
callable.get_bound_arguments_ref(sub_args, sub_count);
252-
253-
r_argcount = sub_args.size() - argcount;
246+
void CallableCustomUnbind::get_bound_arguments(Vector<Variant> &r_arguments) const {
247+
callable.get_bound_arguments_ref(r_arguments);
248+
}
254249

255-
if (argcount >= sub_args.size()) {
256-
r_arguments = Vector<Variant>();
257-
} else {
258-
sub_args.resize(sub_args.size() - argcount);
259-
r_arguments = sub_args;
260-
}
250+
int CallableCustomUnbind::get_unbound_arguments_count() const {
251+
return callable.get_unbound_arguments_count() + argcount;
261252
}
262253

263254
void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {

‎core/variant/callable_bind.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ class CallableCustomBind : public CallableCustom {
5555
virtual const Callable *get_base_comparator() const override;
5656
virtual int get_argument_count(bool &r_is_valid) const override;
5757
virtual int get_bound_arguments_count() const override;
58-
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
58+
virtual void get_bound_arguments(Vector<Variant> &r_arguments) const override;
59+
virtual int get_unbound_arguments_count() const override;
5960
Callable get_callable() { return callable; }
6061
Vector<Variant> get_binds() { return binds; }
6162

@@ -84,7 +85,8 @@ class CallableCustomUnbind : public CallableCustom {
8485
virtual const Callable *get_base_comparator() const override;
8586
virtual int get_argument_count(bool &r_is_valid) const override;
8687
virtual int get_bound_arguments_count() const override;
87-
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
88+
virtual void get_bound_arguments(Vector<Variant> &r_arguments) const override;
89+
virtual int get_unbound_arguments_count() const override;
8890

8991
Callable get_callable() { return callable; }
9092
int get_unbinds() { return argcount; }

‎core/variant/variant.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -3668,18 +3668,20 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
36683668

36693669
String Variant::get_callable_error_text(const Callable &p_callable, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce) {
36703670
Vector<Variant> binds;
3671-
int args_bound;
3672-
p_callable.get_bound_arguments_ref(binds, args_bound);
3673-
if (args_bound <= 0) {
3674-
return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, MAX(0, p_argcount + args_bound), ce);
3671+
p_callable.get_bound_arguments_ref(binds);
3672+
3673+
int args_unbound = p_callable.get_unbound_arguments_count();
3674+
3675+
if (p_argcount - args_unbound < 0) {
3676+
return "Callable unbinds " + itos(args_unbound) + " arguments, but called with " + itos(p_argcount);
36753677
} else {
36763678
Vector<const Variant *> argptrs;
3677-
argptrs.resize(p_argcount + binds.size());
3678-
for (int i = 0; i < p_argcount; i++) {
3679+
argptrs.resize(p_argcount - args_unbound + binds.size());
3680+
for (int i = 0; i < p_argcount - args_unbound; i++) {
36793681
argptrs.write[i] = p_argptrs[i];
36803682
}
36813683
for (int i = 0; i < binds.size(); i++) {
3682-
argptrs.write[i + p_argcount] = &binds[i];
3684+
argptrs.write[i + p_argcount - args_unbound] = &binds[i];
36833685
}
36843686
return get_call_error_text(p_callable.get_object(), p_callable.get_method(), (const Variant **)argptrs.ptr(), argptrs.size(), ce);
36853687
}

‎core/variant/variant_call.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,7 @@ static void _register_variant_builtin_methods_misc() {
21162116
bind_function(Callable, get_argument_count, _VariantCall::func_Callable_get_argument_count, sarray(), varray());
21172117
bind_method(Callable, get_bound_arguments_count, sarray(), varray());
21182118
bind_method(Callable, get_bound_arguments, sarray(), varray());
2119+
bind_method(Callable, get_unbound_arguments_count, sarray(), varray());
21192120
bind_method(Callable, hash, sarray(), varray());
21202121
bind_method(Callable, bindv, sarray("arguments"), varray());
21212122
bind_method(Callable, unbind, sarray("argcount"), varray());

‎doc/classes/Callable.xml

+17-2
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,21 @@
155155
<method name="get_bound_arguments" qualifiers="const">
156156
<return type="Array" />
157157
<description>
158-
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).
158+
Returns the array of arguments bound via successive [method bind] or [method unbind] calls. These arguments will be added [i]after[/i] the arguments passed to the call, from which [method get_unbound_arguments_count] arguments on the right have been previously excluded.
159+
[codeblock]
160+
func get_effective_arguments(callable, call_args):
161+
assert(call_args.size() - callable.get_unbound_arguments_count() &gt;= 0)
162+
var result = call_args.slice(0, call_args.size() - callable.get_unbound_arguments_count())
163+
result.append_array(callable.get_bound_arguments())
164+
return result
165+
[/codeblock]
159166
</description>
160167
</method>
161168
<method name="get_bound_arguments_count" qualifiers="const">
162169
<return type="int" />
163170
<description>
164-
Returns the total amount of arguments bound (or unbound) via successive [method bind] or [method unbind] calls. If the amount of arguments unbound is greater than the ones bound, this function returns a value less than zero.
171+
Returns the total amount of arguments bound via successive [method bind] or [method unbind] calls. This is the same as the size of the array returned by [method get_bound_arguments]. See [method get_bound_arguments] for details.
172+
[b]Note:[/b] The [method get_bound_arguments_count] and [method get_unbound_arguments_count] methods can both return positive values.
165173
</description>
166174
</method>
167175
<method name="get_method" qualifiers="const">
@@ -182,6 +190,13 @@
182190
Returns the ID of this [Callable]'s object (see [method Object.get_instance_id]).
183191
</description>
184192
</method>
193+
<method name="get_unbound_arguments_count" qualifiers="const">
194+
<return type="int" />
195+
<description>
196+
Returns the total amount of arguments unbound via successive [method bind] or [method unbind] calls. See [method get_bound_arguments] for details.
197+
[b]Note:[/b] The [method get_bound_arguments_count] and [method get_unbound_arguments_count] methods can both return positive values.
198+
</description>
199+
</method>
185200
<method name="hash" qualifiers="const">
186201
<return type="int" />
187202
<description>

‎scene/main/node.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -3055,11 +3055,12 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
30553055
if (copy && copytarget && E.callable.get_method() != StringName()) {
30563056
Callable copy_callable = Callable(copytarget, E.callable.get_method());
30573057
if (!copy->is_connected(E.signal.get_name(), copy_callable)) {
3058-
int arg_count = E.callable.get_bound_arguments_count();
3059-
if (arg_count > 0) {
3058+
int unbound_arg_count = E.callable.get_unbound_arguments_count();
3059+
if (unbound_arg_count > 0) {
3060+
copy_callable = copy_callable.unbind(unbound_arg_count);
3061+
}
3062+
if (E.callable.get_bound_arguments_count() > 0) {
30603063
copy_callable = copy_callable.bindv(E.callable.get_bound_arguments());
3061-
} else if (arg_count < 0) {
3062-
copy_callable = copy_callable.unbind(-arg_count);
30633064
}
30643065
copy->connect(E.signal.get_name(), copy_callable, E.flags);
30653066
}

‎tests/core/variant/test_callable.h

+64
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,70 @@ TEST_CASE("[Callable] Argument count") {
135135

136136
memdelete(my_test);
137137
}
138+
139+
class TestBoundUnboundArgumentCount : public Object {
140+
GDCLASS(TestBoundUnboundArgumentCount, Object);
141+
142+
protected:
143+
static void _bind_methods() {
144+
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "test_func", &TestBoundUnboundArgumentCount::test_func, MethodInfo("test_func"));
145+
}
146+
147+
public:
148+
Variant test_func(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
149+
Array result;
150+
result.resize(p_argcount);
151+
for (int i = 0; i < p_argcount; i++) {
152+
result[i] = *p_args[i];
153+
}
154+
return result;
155+
}
156+
157+
static String get_output(const Callable &p_callable) {
158+
Array effective_args;
159+
effective_args.push_back(7);
160+
effective_args.push_back(8);
161+
effective_args.push_back(9);
162+
163+
effective_args.resize(3 - p_callable.get_unbound_arguments_count());
164+
effective_args.append_array(p_callable.get_bound_arguments());
165+
166+
return vformat(
167+
"%d %d %s %s %s",
168+
p_callable.get_unbound_arguments_count(),
169+
p_callable.get_bound_arguments_count(),
170+
p_callable.get_bound_arguments(),
171+
p_callable.call(7, 8, 9),
172+
effective_args);
173+
}
174+
};
175+
176+
TEST_CASE("[Callable] Bound and unbound argument count") {
177+
String (*get_output)(const Callable &) = TestBoundUnboundArgumentCount::get_output;
178+
179+
TestBoundUnboundArgumentCount *test_instance = memnew(TestBoundUnboundArgumentCount);
180+
181+
Callable test_func = Callable(test_instance, "test_func");
182+
183+
CHECK(get_output(test_func) == "0 0 [] [7, 8, 9] [7, 8, 9]");
184+
CHECK(get_output(test_func.bind(1, 2)) == "0 2 [1, 2] [7, 8, 9, 1, 2] [7, 8, 9, 1, 2]");
185+
CHECK(get_output(test_func.bind(1, 2).unbind(1)) == "1 2 [1, 2] [7, 8, 1, 2] [7, 8, 1, 2]");
186+
CHECK(get_output(test_func.bind(1, 2).unbind(1).bind(3, 4)) == "0 3 [3, 1, 2] [7, 8, 9, 3, 1, 2] [7, 8, 9, 3, 1, 2]");
187+
CHECK(get_output(test_func.bind(1, 2).unbind(1).bind(3, 4).unbind(1)) == "1 3 [3, 1, 2] [7, 8, 3, 1, 2] [7, 8, 3, 1, 2]");
188+
189+
CHECK(get_output(test_func.bind(1).bind(2).bind(3).unbind(1)) == "1 3 [3, 2, 1] [7, 8, 3, 2, 1] [7, 8, 3, 2, 1]");
190+
CHECK(get_output(test_func.bind(1).bind(2).unbind(1).bind(3)) == "0 2 [2, 1] [7, 8, 9, 2, 1] [7, 8, 9, 2, 1]");
191+
CHECK(get_output(test_func.bind(1).unbind(1).bind(2).bind(3)) == "0 2 [3, 1] [7, 8, 9, 3, 1] [7, 8, 9, 3, 1]");
192+
CHECK(get_output(test_func.unbind(1).bind(1).bind(2).bind(3)) == "0 2 [3, 2] [7, 8, 9, 3, 2] [7, 8, 9, 3, 2]");
193+
194+
CHECK(get_output(test_func.unbind(1).unbind(1).unbind(1).bind(1, 2, 3)) == "0 0 [] [7, 8, 9] [7, 8, 9]");
195+
CHECK(get_output(test_func.unbind(1).unbind(1).bind(1, 2, 3).unbind(1)) == "1 1 [1] [7, 8, 1] [7, 8, 1]");
196+
CHECK(get_output(test_func.unbind(1).bind(1, 2, 3).unbind(1).unbind(1)) == "2 2 [1, 2] [7, 1, 2] [7, 1, 2]");
197+
CHECK(get_output(test_func.bind(1, 2, 3).unbind(1).unbind(1).unbind(1)) == "3 3 [1, 2, 3] [1, 2, 3] [1, 2, 3]");
198+
199+
memdelete(test_instance);
200+
}
201+
138202
} // namespace TestCallable
139203

140204
#endif // TEST_CALLABLE_H

0 commit comments

Comments
 (0)