Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Fix Callable.get_bound_arguments{,_count}() return incorrect data #98713

Merged

Conversation

dalexeev
Copy link
Member

@dalexeev dalexeev commented Oct 31, 2024

Test script
@tool
extends EditorScript

func f(a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0):
    return [a, b, c, d, e, f, g, h]

#func get_effective_arguments(callable: Callable, call_args: Array) -> Array:
    #assert(call_args.size() - callable.get_unbound_arguments_count() >= 0)
    #var result = call_args.slice(0, call_args.size() - callable.get_unbound_arguments_count())
    #result.append_array(callable.get_bound_arguments())
    #return result

func test(c: Callable) -> void:
    print("%d %d %-9s %s" % [
    #print("%d %d %d %-9s %s %s" % [
        c.get_argument_count(),
        #c.get_unbound_arguments_count(),
        c.get_bound_arguments_count(),
        c.get_bound_arguments(),
        c.call(7, 8, 9),
        #get_effective_arguments(c, [7, 8, 9])
    ])

func _run() -> void:
    test(f)
    test(f.bind(1, 2))
    test(f.bind(1, 2).unbind(1))
    test(f.bind(1, 2).unbind(1).bind(3, 4))
    test(f.bind(1, 2).unbind(1).bind(3, 4).unbind(1))
    print("---")
    test(f.bind(1).bind(2).bind(3).unbind(1))
    test(f.bind(1).bind(2).unbind(1).bind(3))
    test(f.bind(1).unbind(1).bind(2).bind(3))
    test(f.unbind(1).bind(1).bind(2).bind(3))
    print("---")
    test(f.unbind(1).unbind(1).unbind(1).bind(1, 2, 3))
    test(f.unbind(1).unbind(1).bind(1, 2, 3).unbind(1))
    test(f.unbind(1).bind(1, 2, 3).unbind(1).unbind(1))
    test(f.bind(1, 2, 3).unbind(1).unbind(1).unbind(1))

Before:

8 0 []        [7, 8, 9, 0, 0, 0, 0, 0]
6 2 [1, 2]    [7, 8, 9, 1, 2, 0, 0, 0]
7 1 [1]       [7, 8, 1, 2, 0, 0, 0, 0]
5 3 [1, 3, 4] [7, 8, 9, 3, 1, 2, 0, 0]
6 2 [1, 3]    [7, 8, 3, 1, 2, 0, 0, 0]
---
6 2 [1, 2]    [7, 8, 3, 2, 1, 0, 0, 0]
6 2 [1, 3]    [7, 8, 9, 2, 1, 0, 0, 0]
6 2 [2, 3]    [7, 8, 9, 3, 1, 0, 0, 0]
6 2 [2, 3]    [7, 8, 9, 3, 2, 0, 0, 0]
---
8 0 [2, 3]    [7, 8, 9, 0, 0, 0, 0, 0]
8 0 [2]       [7, 8, 1, 0, 0, 0, 0, 0]
8 0 []        [7, 1, 2, 0, 0, 0, 0, 0]
8 0 []        [1, 2, 3, 0, 0, 0, 0, 0]

After:

8 0 0 []        [7, 8, 9, 0, 0, 0, 0, 0] [7, 8, 9]
6 0 2 [1, 2]    [7, 8, 9, 1, 2, 0, 0, 0] [7, 8, 9, 1, 2]
7 1 2 [1, 2]    [7, 8, 1, 2, 0, 0, 0, 0] [7, 8, 1, 2]
5 0 3 [3, 1, 2] [7, 8, 9, 3, 1, 2, 0, 0] [7, 8, 9, 3, 1, 2]
6 1 3 [3, 1, 2] [7, 8, 3, 1, 2, 0, 0, 0] [7, 8, 3, 1, 2]
---
6 1 3 [3, 2, 1] [7, 8, 3, 2, 1, 0, 0, 0] [7, 8, 3, 2, 1]
6 0 2 [2, 1]    [7, 8, 9, 2, 1, 0, 0, 0] [7, 8, 9, 2, 1]
6 0 2 [3, 1]    [7, 8, 9, 3, 1, 0, 0, 0] [7, 8, 9, 3, 1]
6 0 2 [3, 2]    [7, 8, 9, 3, 2, 0, 0, 0] [7, 8, 9, 3, 2]
---
8 0 0 []        [7, 8, 9, 0, 0, 0, 0, 0] [7, 8, 9]
8 1 1 [1]       [7, 8, 1, 0, 0, 0, 0, 0] [7, 8, 1]
8 2 2 [1, 2]    [7, 1, 2, 0, 0, 0, 0, 0] [7, 1, 2]
8 3 3 [1, 2, 3] [1, 2, 3, 0, 0, 0, 0, 0] [1, 2, 3]

Note: This is probably unrelated and may not be a bug, but get_argument_count() can return negative values ​​for variadic functions, as in the test.

@dalexeev dalexeev added this to the 4.4 milestone Oct 31, 2024
@dalexeev dalexeev requested review from a team as code owners October 31, 2024 23:29
@dalexeev dalexeev force-pushed the core-fix-callable-get-bound-arguments branch from e305498 to 423aca0 Compare October 31, 2024 23:37
@jinyangcruise
Copy link

Thanks very much for your quick fix.

BTW, I just figured out that unbind() can not only discard the arguments passed to the call() or callv(), but also the arguments bound by the bind() which is chained after unbind(). I will mention this under my issue page because I wrote that

So my opinion is `get_bound_arguments_count` and `get_bound_arguments` should not be influenced by unbind() calls.

which is not that accurate.

@dalexeev dalexeev force-pushed the core-fix-callable-get-bound-arguments branch from 423aca0 to 7f65c4d Compare November 1, 2024 07:15
@dalexeev
Copy link
Member Author

dalexeev commented Nov 1, 2024

I will try to give a formal proof of why this works and why we first remove unbound arguments and then add bound ones, and not vice versa.

1. Successive calls of the same name can be reduced to the form .bind(...).unbind(...).

1.1. .bind(A).bind(B) can be reduced to .bind(B, A), i.e. .bind(B, A).unbind(0).

1.2. .unbind(N).unbind(M) can be reduced to .unbind(N + M), i.e. .bind().unbind(N + M).

2. Successive calls of different names can be reduced to the form .bind(...).unbind(...).

2.1. .bind(A).unbind(N) cannot be reduced.

2.2. .unbind(N).bind(A) can be reduced to .unbind(N').bind(A'), where:

R = min(N, A.size())
N' = N - R
A' = A.slice(0, A.size() - R)

Since R = min(N, A.size()), either N' = 0 or A'.size() = 0.

So, .unbind(N).bind(A) can be reduced either to .unbind(N') or .bind(A'), i.e. bind().unbind(N') or .bind(A').unbind(0).

3. Any sequence of bind() and unbind() calls can be reduced to the form .bind(...).unbind(...).

.bind(A).unbind(N).bind(B).unbind(M) can be reduced to .bind(A).unbind(N').bind(B').unbind(M) (2.2).

So, it will be either .bind(A).unbind(N').unbind(M) or .bind(A).bind(B').unbind(M) (2.2).

So, it will be either .bind(A).unbind(N' + M) (1.2) or .bind(B', A).unbind(M) (1.1).

@jinyangcruise
Copy link

Very impresive! I hope your summarization can be added on the Godot's documentation which can help many users to better understand the design of bind and unbind.

Copy link
Member

@AThousandShips AThousandShips left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The theory checks out and the tests provided covers all the cases I think are necessary, didn't do any deeper analysis of the code at this time but it looks good to me!

Copy link
Member

@kleonc kleonc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming the current behavior of chained Callable.bind/Callable.unbind calls is correct / we don't want to change that, this fix looks correct to me.
The previous behavior of Callable.get_bound_arguments / Callable.get_bound_arguments_count makes not much sense for chained binds/unbinds indeed.

The theory in #98713 (comment) looks good, code looks fine too. I've tested it some further in action and everything seems to work as expected. 👍

Test script

@tool
extends EditorScript

func foo(a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0):
	return [a, b, c, d, e, f, g, h]
var foo_lambda: Callable = func (a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0):
	return [a, b, c, d, e, f, g, h]

func test(c: Callable, repr: String = "") -> void:
	print("%-18s %-22s  %2d %2d %2d  %-9s  %s" % [
		repr,
		".call(5, 6, 7, 8, 9)" if c.get_unbound_arguments_count() <= 5 else "CAN'T .call(5, 6, 7, 8, 9)",
		c.get_argument_count(),
		c.get_unbound_arguments_count(),
		c.get_bound_arguments_count(),
		c.get_bound_arguments(),
		c.call(5, 6, 7, 8, 9) if c.get_unbound_arguments_count() <= 5 else "N/A",
	])
	##4.3
	#print("%-18s %-22s  %2d %2s %2d  %-9s  %s" % [
		#repr,
		#".call(5, 6, 7, 8, 9)",
		#c.get_argument_count(),
		#"-",
		#c.get_bound_arguments_count(),
		#c.get_bound_arguments(),
		#c.call(5, 6, 7, 8, 9),
	#])

func test_subcalls(c: Callable, subcalls: Array[Subcall]) -> void:
	var repr: String = c.get_method()
	test(c, repr)
	for i in subcalls.size():
		var subcall: Subcall = subcalls[i]
		c = subcall.apply_to(c)
		repr = "%s%s" % [" ".repeat(i), subcall]
		test(c, repr)
	print_separator()

func print_separator() -> void:
	print("-".repeat(90))


func Bind(args: Array) -> Subcall: return Subcall.new(Subcall.Type.BIND, args)
func Unbind(count: int) -> Subcall: return Subcall.new(Subcall.Type.UNBIND, [count])

class Subcall:
	enum Type { BIND, UNBIND }
	var type: Type
	var args: Array
	
	func _init(type: Type, args: Array) -> void:
		self.type = type
		self.args = args
	
	func _to_string() -> String:
		return ".%s(%s)" % [
			"bind" if type == Type.BIND else "unbind",
			", ".join(args),
		]
	
	func apply_to(c: Callable) -> Callable:
		if type == Type.BIND:
			return c.bindv(args)
		else:
			return c.unbind(args[0])


func _run() -> void:
	var f: Callable = foo
	#var f: Callable = foo_lambda
	
	test_subcalls(f, [
		Bind([1, 2]),
		Unbind(1),
		Bind([3, 4]),
		Unbind(1),
	])

	for unbind_arg in [1, 3, 5]:
		print_separator()
		test_subcalls(f, [
			Bind([1]),
			Bind([2]),
			Bind([3]),
			Unbind(unbind_arg),
		])
		test_subcalls(f, [
			Bind([1]),
			Bind([2]),
			Unbind(unbind_arg),
			Bind([3]),
		])
		test_subcalls(f, [
			Bind([1]),
			Unbind(unbind_arg),
			Bind([2]),
			Bind([3]),
		])
		test_subcalls(f, [
			Unbind(unbind_arg),
			Bind([1]),
			Bind([2]),
			Bind([3]),
		])
	
	print_separator()
	test_subcalls(f, [
		Unbind(1),
		Unbind(1),
		Unbind(1),
		Bind([1, 2, 3]),
	])
	test_subcalls(f, [
		Unbind(1),
		Unbind(1),
		Bind([1, 2, 3]),
		Unbind(1),
	])
	test_subcalls(f, [
		Unbind(1),
		Bind([1, 2, 3]),
		Unbind(1),
		Unbind(1),
	])
	test_subcalls(f, [
		Bind([1, 2, 3]),
		Unbind(1),
		Unbind(1),
		Unbind(1),
	])
	
	print_separator()
	test_subcalls(f, [
		Bind([1]),
		Unbind(2),
		Bind([2]),
		Unbind(2),
		Bind([3]),
		Unbind(2),
	])

Output (this PR)

foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1, 2)        .call(5, 6, 7, 8, 9)     6  0  2  [1, 2]     [5, 6, 7, 8, 9, 1, 2, 0]
 .unbind(1)        .call(5, 6, 7, 8, 9)     7  1  2  [1, 2]     [5, 6, 7, 8, 1, 2, 0, 0]
  .bind(3, 4)      .call(5, 6, 7, 8, 9)     5  0  3  [3, 1, 2]  [5, 6, 7, 8, 9, 3, 1, 2]
   .unbind(1)      .call(5, 6, 7, 8, 9)     6  1  3  [3, 1, 2]  [5, 6, 7, 8, 3, 1, 2, 0]
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .bind(2)          .call(5, 6, 7, 8, 9)     6  0  2  [2, 1]     [5, 6, 7, 8, 9, 2, 1, 0]
  .bind(3)         .call(5, 6, 7, 8, 9)     5  0  3  [3, 2, 1]  [5, 6, 7, 8, 9, 3, 2, 1]
   .unbind(1)      .call(5, 6, 7, 8, 9)     6  1  3  [3, 2, 1]  [5, 6, 7, 8, 3, 2, 1, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .bind(2)          .call(5, 6, 7, 8, 9)     6  0  2  [2, 1]     [5, 6, 7, 8, 9, 2, 1, 0]
  .unbind(1)       .call(5, 6, 7, 8, 9)     7  1  2  [2, 1]     [5, 6, 7, 8, 2, 1, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)     6  0  2  [2, 1]     [5, 6, 7, 8, 9, 2, 1, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .unbind(1)        .call(5, 6, 7, 8, 9)     8  1  1  [1]        [5, 6, 7, 8, 1, 0, 0, 0]
  .bind(2)         .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)     6  0  2  [3, 1]     [5, 6, 7, 8, 9, 3, 1, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.unbind(1)         .call(5, 6, 7, 8, 9)     9  1  0  []         [5, 6, 7, 8, 0, 0, 0, 0]
 .bind(1)          .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
  .bind(2)         .call(5, 6, 7, 8, 9)     7  0  1  [2]        [5, 6, 7, 8, 9, 2, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)     6  0  2  [3, 2]     [5, 6, 7, 8, 9, 3, 2, 0]
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .bind(2)          .call(5, 6, 7, 8, 9)     6  0  2  [2, 1]     [5, 6, 7, 8, 9, 2, 1, 0]
  .bind(3)         .call(5, 6, 7, 8, 9)     5  0  3  [3, 2, 1]  [5, 6, 7, 8, 9, 3, 2, 1]
   .unbind(3)      .call(5, 6, 7, 8, 9)     8  3  3  [3, 2, 1]  [5, 6, 3, 2, 1, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .bind(2)          .call(5, 6, 7, 8, 9)     6  0  2  [2, 1]     [5, 6, 7, 8, 9, 2, 1, 0]
  .unbind(3)       .call(5, 6, 7, 8, 9)     9  3  2  [2, 1]     [5, 6, 2, 1, 0, 0, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)     8  2  2  [2, 1]     [5, 6, 7, 2, 1, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .unbind(3)        .call(5, 6, 7, 8, 9)    10  3  1  [1]        [5, 6, 1, 0, 0, 0, 0, 0]
  .bind(2)         .call(5, 6, 7, 8, 9)     9  2  1  [1]        [5, 6, 7, 1, 0, 0, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)     8  1  1  [1]        [5, 6, 7, 8, 1, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.unbind(3)         .call(5, 6, 7, 8, 9)    11  3  0  []         [5, 6, 0, 0, 0, 0, 0, 0]
 .bind(1)          .call(5, 6, 7, 8, 9)    10  2  0  []         [5, 6, 7, 0, 0, 0, 0, 0]
  .bind(2)         .call(5, 6, 7, 8, 9)     9  1  0  []         [5, 6, 7, 8, 0, 0, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .bind(2)          .call(5, 6, 7, 8, 9)     6  0  2  [2, 1]     [5, 6, 7, 8, 9, 2, 1, 0]
  .bind(3)         .call(5, 6, 7, 8, 9)     5  0  3  [3, 2, 1]  [5, 6, 7, 8, 9, 3, 2, 1]
   .unbind(5)      .call(5, 6, 7, 8, 9)    10  5  3  [3, 2, 1]  [3, 2, 1, 0, 0, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .bind(2)          .call(5, 6, 7, 8, 9)     6  0  2  [2, 1]     [5, 6, 7, 8, 9, 2, 1, 0]
  .unbind(5)       .call(5, 6, 7, 8, 9)    11  5  2  [2, 1]     [2, 1, 0, 0, 0, 0, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)    10  4  2  [2, 1]     [5, 2, 1, 0, 0, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .unbind(5)        .call(5, 6, 7, 8, 9)    12  5  1  [1]        [1, 0, 0, 0, 0, 0, 0, 0]
  .bind(2)         .call(5, 6, 7, 8, 9)    11  4  1  [1]        [5, 1, 0, 0, 0, 0, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)    10  3  1  [1]        [5, 6, 1, 0, 0, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.unbind(5)         .call(5, 6, 7, 8, 9)    13  5  0  []         [0, 0, 0, 0, 0, 0, 0, 0]
 .bind(1)          .call(5, 6, 7, 8, 9)    12  4  0  []         [5, 0, 0, 0, 0, 0, 0, 0]
  .bind(2)         .call(5, 6, 7, 8, 9)    11  3  0  []         [5, 6, 0, 0, 0, 0, 0, 0]
   .bind(3)        .call(5, 6, 7, 8, 9)    10  2  0  []         [5, 6, 7, 0, 0, 0, 0, 0]
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.unbind(1)         .call(5, 6, 7, 8, 9)     9  1  0  []         [5, 6, 7, 8, 0, 0, 0, 0]
 .unbind(1)        .call(5, 6, 7, 8, 9)    10  2  0  []         [5, 6, 7, 0, 0, 0, 0, 0]
  .unbind(1)       .call(5, 6, 7, 8, 9)    11  3  0  []         [5, 6, 0, 0, 0, 0, 0, 0]
   .bind(1, 2, 3)  .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.unbind(1)         .call(5, 6, 7, 8, 9)     9  1  0  []         [5, 6, 7, 8, 0, 0, 0, 0]
 .unbind(1)        .call(5, 6, 7, 8, 9)    10  2  0  []         [5, 6, 7, 0, 0, 0, 0, 0]
  .bind(1, 2, 3)   .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
   .unbind(1)      .call(5, 6, 7, 8, 9)     8  1  1  [1]        [5, 6, 7, 8, 1, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.unbind(1)         .call(5, 6, 7, 8, 9)     9  1  0  []         [5, 6, 7, 8, 0, 0, 0, 0]
 .bind(1, 2, 3)    .call(5, 6, 7, 8, 9)     6  0  2  [1, 2]     [5, 6, 7, 8, 9, 1, 2, 0]
  .unbind(1)       .call(5, 6, 7, 8, 9)     7  1  2  [1, 2]     [5, 6, 7, 8, 1, 2, 0, 0]
   .unbind(1)      .call(5, 6, 7, 8, 9)     8  2  2  [1, 2]     [5, 6, 7, 1, 2, 0, 0, 0]
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1, 2, 3)     .call(5, 6, 7, 8, 9)     5  0  3  [1, 2, 3]  [5, 6, 7, 8, 9, 1, 2, 3]
 .unbind(1)        .call(5, 6, 7, 8, 9)     6  1  3  [1, 2, 3]  [5, 6, 7, 8, 1, 2, 3, 0]
  .unbind(1)       .call(5, 6, 7, 8, 9)     7  2  3  [1, 2, 3]  [5, 6, 7, 1, 2, 3, 0, 0]
   .unbind(1)      .call(5, 6, 7, 8, 9)     8  3  3  [1, 2, 3]  [5, 6, 1, 2, 3, 0, 0, 0]
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
foo                .call(5, 6, 7, 8, 9)     8  0  0  []         [5, 6, 7, 8, 9, 0, 0, 0]
.bind(1)           .call(5, 6, 7, 8, 9)     7  0  1  [1]        [5, 6, 7, 8, 9, 1, 0, 0]
 .unbind(2)        .call(5, 6, 7, 8, 9)     9  2  1  [1]        [5, 6, 7, 1, 0, 0, 0, 0]
  .bind(2)         .call(5, 6, 7, 8, 9)     8  1  1  [1]        [5, 6, 7, 8, 1, 0, 0, 0]
   .unbind(2)      .call(5, 6, 7, 8, 9)    10  3  1  [1]        [5, 6, 1, 0, 0, 0, 0, 0]
    .bind(3)       .call(5, 6, 7, 8, 9)     9  2  1  [1]        [5, 6, 7, 1, 0, 0, 0, 0]
     .unbind(2)    .call(5, 6, 7, 8, 9)    11  4  1  [1]        [5, 1, 0, 0, 0, 0, 0, 0]
------------------------------------------------------------------------------------------


Some core folks should take a look at this before merging though (I'm not sure if everything needed was changed, if it won't break extensions etc.).

core/variant/callable_bind.cpp Outdated Show resolved Hide resolved
@AThousandShips
Copy link
Member

Assuming the current behavior of chained Callable.bind/Callable.unbind calls is correct / we don't want to change that, this fix looks correct to me.

I think we can evaluate down the line exactly the desired behavior, but I think maintaining the behavior of those, as it is at least useful, is more important than these methods, I would say more people would be affected by changes to the binding behavior

@dalexeev dalexeev force-pushed the core-fix-callable-get-bound-arguments branch from 7f65c4d to e379cc7 Compare November 4, 2024 19:42
@Repiteo Repiteo merged commit cc6ee3e into godotengine:master Nov 12, 2024
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Nov 12, 2024

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

get_bound_arguments is wrong after using unbind
5 participants