godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.82k stars 21.14k forks source link

`get_bound_arguments` is wrong after using `unbind` #98695

Open jinyangcruise opened 6 days ago

jinyangcruise commented 6 days ago

Tested versions

System information

Windows 11, Vulkan API 1.3.280 - Forward+ - Using Vulkan Device #0: NVIDIA - NVIDIA GeForce RTX 3060 Ti

Issue description

The doc of get_bound_arguments_count says:

Returns the total amount of arguments bound (or unbound) via successive bind() or unbind() calls. 
If the amount of arguments unbound is greater than the ones bound, this function returns a value less than zero.

So we can guess that get_bound_arguments also influenced by unbind() calls.

They bahave just as what the doc says however not be consistent with the real bound arguments used by Callable at runtime. See the code:

extends Node2D

func test(a, b, c, d):
    printt("a:", a)
    printt("b:", b)
    printt("c:", c)
    printt("d:", d)

func _ready() -> void:
    var callable = test.bind(1, 2).unbind(1)
    callable.call(7, 8, 9)
    printt("get_bound_arguments:", callable.get_bound_arguments())

The output is:

a:  7
b:  8
c:  1
d:  2
get_bound_arguments:    [1]

The output shows that the unbind() call only discard the arguments we passed to the callable and does NOT remove previously bound arguments. In the case above, 9 is discard by unbind(1) and 1, 2 are still bound to the last two arguments c and d. This is not consistent with the result of c.get_bound_arguments() which says there is only one argument bound and the value is 1 however we all know that there are two arguments are bound (1 to c and 2 to d).

In other words, if get_bound_arguments returning [1] is correct, then we are being told that 1 is bound to the argument d which is absolutely wrong.

From another perspective, isn't the purpose of this method to tell us the real bound arguments used by a Callable? If not, what else can we use to get such information?

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

--------------------------Update----------------------------:

The conclusion

get_bound_arguments_count ` and `get_bound_arguments` should not be influenced by unbind() calls.

is not correct because unbind() calls can still "unbind" the arguments bound by a following bind() call. One example is

var callable = func(a = 1): printt(a)
callable.unbind(1).bind(9).call() # Output is 1, not 9.

So the unbind doesn't remove previously bound arguments but will remove following bound arguments.

Steps to reproduce

Use the code as a script of a 2D node scene and run.

extends Node2D

func test(a, b, c, d):
    printt("a:", a)
    printt("b:", b)
    printt("c:", c)
    printt("d:", d)

func _ready() -> void:
    var callable = test.bind(1, 2).unbind(1)
    callable.call(7, 8, 9)
    printt("get_bound_arguments:", callable.get_bound_arguments())

Minimal reproduction project (MRP)

NA

jinyangcruise commented 5 days ago

I just find 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().

So the conclusion

get_bound_arguments_count ` and `get_bound_arguments` should not be influenced by unbind() calls.

is not correct because unbind() calls can still "unbind" the arguments bound by a following bind() call. One example is:

var callable = func(a = 1): printt(a)
callable.unbind(1).bind(9).call() # Output is 1, not 9.

So the unbind doesn't remove previously bound arguments but will remove following bound arguments if there is a following bind.

NOTE: this does not mean get_bound_arguments_count and get_bound_arguments are totally correct and thanks dalexeev for looking into it and made a fix.