You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
4.2.1.stable outputs error message Attempt to call function 'null::<function name> (Callable)' on a null instance. and 4.2.stable has no outputs on it.
System information
Godot v4.2.1.stable - macOS 14.3.1 - Vulkan (Forward+) - integrated Apple M2 Max - Apple M2 Max (12 Threads)
Issue description
Callable instances of a static function, retrieved directly from the class, have strange & inconsistent behaviors.
Classes Used in Test
Suppose a user defined (inner or global, by class or class_name will have the same behavior regarding this issue) class is claimed as the following, and then MyClass should be an instance of GDScript:
And the built-in classes as GDScriptNativeClass instances also have many static functions, take class Node and its static function print_orphan_nodes() as an example.
Behavior Observation and Conclusion
Details of the testing results please see the steps section.
The callable instance of functions print_orphan_nodes and my_static will have the following behaviors:
User Classes
For user defined classes, using a string name to retrieve the method directly from the class (instead of an instance) will get a callable on null object.
Callables obtained by MyClass.my_static and my_instance.my_static work normally.
Callables obtained by my_instance["my_static"] and Callable(my_instance, "my_static") work normally.
Callables obtained by MyClass["my_static"] and Callable(MyClass, "my_static") will raise error Attempt to call function 'null::my_static (Callable)' on a null instance..
However, explicitly castingMyClass as any of its ancestor built-in classes' instance (GDScript, Script, ..., Object) will allow the string name to work, like MyClass as GDScript.
Native Classes
For built-in classes, the callables obtained from an instance still works in all the cases, but from the class itself the behaviors get even more confusing:
Static methods are even not considered as a member of the class itself, checking with the in operator.
Thus, you cannot obtain any callable (even not like in the user defined classes, an invalid/unusable callable null::my_static) by Node.print_orphan_nodes, Node["print_orphan_nodes"], (Node as Object).print_orphan_nodes, (Node as Object)["print_orphan_nodes"].
However, you can find and successfully call directly by Node.print_orphan_nodes() (how weird given that Node.print_orphan_nodes doesn't even exist!)
And strangely, you can create callable instance Callable(Node, "print_orphan_nodes") and call it with no problem.
Moreover, for non-static methods on Object...
Might be related to this #77567 issue. Since those classes are instances of GDScript and GDScriptNativeClass, and therefore instances of Object, they should have access to Object's instance method, taking get_method_list as an example here.
I tested on user and native classes and their behavior also have discrepancy.
In user classes and native classes, get_method_list is confirmed to be a member of both the classes themselves.
In user classes,
For string name retrived callables, the behavior is mostly the same as static method defined on the user class: callables on null object, and can be fixed by casting.
Noticable difference:
Directly calling MyClass.get_method_list() raises a parse error (since the parser thinks it's not a static function and cannot be called on a class)
But geting the callable MyClass.get_method_list first and then .call() on it will successfully execute.
Or, it can be fixed by casting and reminding the parser MyClass is an Object: (MyClass as Object).get_method_list() will work.
Therefore, there are some ways to obtain valid callables:
MyClass.get_method_list
(MyClass as Object).get_method_list
(MyClass as Object)["get_method_list"]
Callable(MyClass as Object, "get_method_list")
In native classes,
All the aforementioned ways will be able to obtain a seemingly valid callable GDScriptNativeClass::get_method_list, but none of them are really "callable". Calling them will all generate Nonexistent function error.
Compared to user classes:
Directly calling Node.get_method_list() also raises Parse Error as the same.
However, not like in user classes, Node.get_method_list.call() won't fix it, as mentioned before, it is a fake callable on nonexistent function.
The only way to run the function immediately is by casting: (Node as Object).get_method_list()
But I don't see any way to produce a valid callable for storing and passing the function reference. Not like in user classes. All will raise nonexistent function error.
It is strange given that (Node as Object).get_method_list() can run, but (Node as Object).get_method_list.call() is nonexistent.
Steps to reproduce
User Classes
@toolextendsEditorScriptfunc_run() ->void:
varmy_instance:=MyClass.new()
print(MyClass) # <GDScript#-9223365398814983298>print(my_instance) # <RefCounted#-9223364908316299205># ------------- Direct calls from identifier -------------MyClass.my_static() # success my_instance.my_static() # success (with warning)# ------------- Callables from identifier ------------- # From classvarcallable_from_class:=MyClass.my_staticprint(callable_from_class) # GDScript::my_staticcallable_from_class.call() # success# From instancevarcallable_from_instance:=my_instance.my_staticprint(callable_from_instance) # RefCounted::my_staticcallable_from_instance.call() # success (with no warning)# ------------- Callables from string name ------------- # Check if MyClass and my_instance has member "my_static"print("my_static"inMyClass) # trueprint("my_static"inmy_instance) # true# ------------- Callables from index ------------- varcallable_from_index:=MyClass["my_static"]
print(callable_from_index) # null::my_static# error: Attempt to call function 'null::my_static (Callable)' on a null instance.# callable_from_index.call() callable_from_index=my_instance["my_static"]
print(callable_from_index) # RefCounted::my_staticcallable_from_index.call() # success (with no warning)# ------------- Callables from constructor ------------- varcallable_from_constructor:=Callable(MyClass, "my_static")
print(callable_from_constructor) # null::my_static# error: Attempt to call function 'null::my_static (Callable)' on a null instance.# callable_from_constructor.call() callable_from_object=Callable(my_instance, "my_static")
print(callable_from_object) # RefCounted::my_staticcallable_from_object.call() # success (with no warning)# ------------- Callables from cast class ------------- # Store the cast class,# or using (MyClass as GDScript) in place will work as the same.# Do not have to be GDScript,# All builtin ancestor classes can work, such as Object.varcast_class: GDScript=MyClassvarcallable_from_cast_class: Callable=cast_class["my_static"]
print(callable_from_cast_class) # GDScript::my_staticcallable_from_cast_class.call() # successcallable_from_cast_class= (MyClassasObject)["my_static"]
print(callable_from_cast_class) # GDScript::my_static <- will still be GDScript instancecallable_from_cast_class.call() # success# Creating callables using the cast class as its object will also work.callable_from_cast_class=Callable(cast_class, "my_static")
print(callable_from_cast_class) # GDScript::my_staticcallable_from_cast_class.call() # success
Native Classes
@toolextendsEditorScriptfunc_run() ->void:
varnode_instance:=Node.new()
print(Node) # <GDScriptNativeClass#-9223372023818878356>print(node_instance) # <Node#20613024514636># ------------- Direct calls from identifier -------------Node.print_orphan_nodes() # success node_instance.print_orphan_nodes() # success (with warning)# ------------- Callables from identifier ------------- # Cannot find from class# error: Invalid get index 'print_orphan_nodes' (on base: 'Node').#var callable_from_class := Node.print_orphan_nodes# From instancevarcallable_from_instance:=node_instance.print_orphan_nodesprint(callable_from_instance) # Node::print_orphan_nodescallable_from_instance.call() # success (with no warning)# ------------- Callables from string name ------------- # Check if Node and node_instance has member "print_orphan_nodes"print("print_orphan_nodes"inNode) # false, but why?print("print_orphan_nodes"innode_instance) # true# ------------- Callables from index ------------- # error: Invalid get index 'print_orphan_nodes' (on base: 'Node').#var callable_from_index: Callable = Node["print_orphan_nodes"]varcallable_from_index: Callable=node_instance["print_orphan_nodes"]
print(callable_from_index) # Node::print_orphan_nodescallable_from_index.call() # success (with no warning)# ------------- Callables from constructor ------------- varcallable_from_constructor:=Callable(Node, "print_orphan_nodes")
print(callable_from_constructor) # GDScriptNativeClass::print_orphan_nodescallable_from_constructor.call() # success!callable_from_object=Callable(node_instance, "print_orphan_nodes")
print(callable_from_object) # Node::print_orphan_nodescallable_from_object.call() # success (with no warning)# ------------- Callables from cast class ------------- # Store the cast class,varcast_class: Object=Node# Still cannot find from class#var callable_from_cast_class: Callable = cast_class["print_orphan_nodes"]#var callable_from_cast_class: Callable = cast_class.print_orphan_nodes# Only creating callable instance from cast class works the samevarcallable_from_cast_class:=Callable(cast_class, "print_orphan_nodes")
print(callable_from_cast_class) # GDScriptNativeClass::print_orphan_nodescallable_from_cast_class.call() # success
Object Instance Method
# Directly call from MyClass or Node will not succeed, # even when they are instances of Objectprint(MyClassisObject) # trueprint(NodeisObject) # true# Parse Error: Cannot call non-static function "get_method_list()" # on the class "MyClass" directly. Make an instance instead.#MyClass.get_method_list() # error#Node.get_method_list() # error# Checking if the classes have the memberprint("get_method_list"inMyClass) # trueprint("get_method_list"inNode) # truevartest: Callable# Trying to call the two callables below will raise error:# Attempt to call function 'null::get_method_list (Callable)' on a null instance.test=MyClass["get_method_list"] # null::get_method_listtest=Callable(MyClass, "get_method_list") # null::get_method_list# And the following three will succeedtest=MyClass.get_method_list# GDScript::get_method_listtest= (MyClassasObject).get_method_list# GDScript::get_method_listtest= (MyClassasObject)["get_method_list"] # GDScript::get_method_listtest=Callable(MyClassasObject, "get_method_list") # GDScript::get_method_list# Trying to call all the callables below will raise error:# Invalid call. Nonexistent function 'GDScriptNativeClass::get_method_list (Callable)'.test=Node["get_method_list"] # GDScriptNativeClass::get_method_listtest=Callable(Node, "get_method_list") # GDScriptNativeClass::get_method_listtest=Node.get_method_list# GDScriptNativeClass::get_method_listtest= (NodeasObject).get_method_list# GDScriptNativeClass::get_method_listtest= (NodeasObject)["get_method_list"] # GDScriptNativeClass::get_method_listtest=Callable(NodeasObject, "get_method_list")
# Explicitly casting as Object will allow calling
(MyClassasObject).get_method_list() # success
(NodeasObject).get_method_list() # success
Minimal reproduction project (MRP)
N/A
The text was updated successfully, but these errors were encountered:
lethefrost
changed the title
Callable instances created from classes' static method have suspect behaviors, and inconsistent in user defined & native classes
Callable obtained from classes (static and non-static methods) have suspect and inconsistent behaviors in user defined & native classes
Mar 10, 2024
yes, my PR only adresses the callables from static methods on native classes, "print_orphan_nodes" in Node and others are unaffected
im not sure about the cause of those ones
Tested versions
4.2.1.stable
,4.2.stable
4.2.1.stable
outputs error messageAttempt to call function 'null::<function name> (Callable)' on a null instance.
and4.2.stable
has no outputs on it.System information
Godot v4.2.1.stable - macOS 14.3.1 - Vulkan (Forward+) - integrated Apple M2 Max - Apple M2 Max (12 Threads)
Issue description
Callable instances of a static function, retrieved directly from the class, have strange & inconsistent behaviors.
Classes Used in Test
Suppose a user defined (inner or global, by
class
orclass_name
will have the same behavior regarding this issue) class is claimed as the following, and thenMyClass
should be an instance ofGDScript
:And the built-in classes as
GDScriptNativeClass
instances also have many static functions, take classNode
and its static functionprint_orphan_nodes()
as an example.Behavior Observation and Conclusion
Details of the testing results please see the steps section.
The callable instance of functions
print_orphan_nodes
andmy_static
will have the following behaviors:User Classes
null
object.MyClass.my_static
andmy_instance.my_static
work normally.my_instance["my_static"]
andCallable(my_instance, "my_static")
work normally.MyClass["my_static"]
andCallable(MyClass, "my_static")
will raise errorAttempt to call function 'null::my_static (Callable)' on a null instance.
.MyClass
as any of its ancestor built-in classes' instance (GDScript
,Script
, ...,Object
) will allow the string name to work, likeMyClass as GDScript
.Native Classes
in
operator.null::my_static
) byNode.print_orphan_nodes
,Node["print_orphan_nodes"]
,(Node as Object).print_orphan_nodes
,(Node as Object)["print_orphan_nodes"]
.Node.print_orphan_nodes()
(how weird given thatNode.print_orphan_nodes
doesn't even exist!)Callable(Node, "print_orphan_nodes")
and call it with no problem.Moreover, for non-static methods on
Object
...Might be related to this #77567 issue. Since those classes are instances of
GDScript
andGDScriptNativeClass
, and therefore instances ofObject
, they should have access toObject
's instance method, takingget_method_list
as an example here.I tested on user and native classes and their behavior also have discrepancy.
get_method_list
is confirmed to be a member of both the classes themselves.null
object, and can be fixed by casting.MyClass.get_method_list()
raises a parse error (since the parser thinks it's not a static function and cannot be called on a class)MyClass.get_method_list
first and then.call()
on it will successfully execute.MyClass
is anObject
:(MyClass as Object).get_method_list()
will work.MyClass.get_method_list
(MyClass as Object).get_method_list
(MyClass as Object)["get_method_list"]
Callable(MyClass as Object, "get_method_list")
GDScriptNativeClass::get_method_list
, but none of them are really "callable". Calling them will all generateNonexistent function
error.Node.get_method_list()
also raises Parse Error as the same.Node.get_method_list.call()
won't fix it, as mentioned before, it is a fake callable on nonexistent function.(Node as Object).get_method_list()
(Node as Object).get_method_list()
can run, but(Node as Object).get_method_list.call()
is nonexistent.Steps to reproduce
User Classes
Native Classes
Object Instance Method
Minimal reproduction project (MRP)
N/A
The text was updated successfully, but these errors were encountered: