-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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
GDScript: Fix some bugs with static variables and functions #77129
GDScript: Fix some bugs with static variables and functions #77129
Conversation
5a0d17c
to
6923c0d
Compare
6923c0d
to
c9bd216
Compare
ed25748
to
c7f311d
Compare
c7f311d
to
36c1ec1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall it looks good, just a couple of things can be improved.
member_property_setter_function = minfo.setter; | ||
member_property_has_setter = member_property_setter_function != StringName(); | ||
member_property_is_in_setter = member_property_has_setter && member_property_setter_function == codegen.function_name; | ||
static_var_class = codegen.add_constant(scr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be problematic if scr
refers to the script being compiled, because it will make a reference to itself so it will never be freed. In such case it should use a CLASS
address instead to use the current script.
There are other instances of this pattern in the compiler, all of them have the same issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that the method can be inherited (see #77331) in which case CLASS
will point to the current class, not the one the static variable is located. This will result in an error (the child class variable with the same index will be used or the GD_ERR_BREAK
index check will be triggered).
So the CLASS
address can only be used in the static initializer, since in this case the current class always matches.
Also, if these constants are counted, then we can still have problems in case of cyclic references. But I think they shouldn't count because it's GDScript*
(not Ref<GDScript>
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it, it does add an extra reference and doesn't depend on GDScript*
vs Ref<GDScript>
(some Variant
/Object
magic?). But we could store ObjectID
instead of GDScript*
(this is how WeakRef
is implemented).
I also found that we already have an extra reference to each script somewhere, the bug is reproduced in master and in 4.0.3. Later I will check/open an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it, it does add an extra reference and doesn't depend on
GDScript*
vsRef<GDScript>
(someVariant
/Object
magic?)
All constants are Variants, if you put a RefCounted object it will increment the count (it does not matter if the Ref<>
wrapper is used, the Variant itself act as the wrapper).
I also found that we already have an extra reference to each script somewhere, the bug is reproduced in master and in 4.0.3. Later I will check/open an issue.
I mentioned this in the static variables PR. But I don't think there's an open issue yet. There's a bit of weird stuff happening in the GDScriptCache so the issue is a bit complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that the method can be inherited (see #77331) in which case
CLASS
will point to the current class, not the one the static variable is located
What I meant is to use CLASS
only in the case where it is in current class. If it is from a super class then it's fine because there's already a reference to it anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mentioned this in the static variables PR. But I don't think there's an open issue yet.
c16bfeb
to
fc30e2f
Compare
gen->write_assign(temp, to_assign); | ||
} | ||
gen->write_set_static_variable(temp, static_var_class, static_var_index); | ||
gen->pop_temporary(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me worry about performance (the need for intermediate temporaries) and maintainability (with this approach, we have to duplicate a lot of tests for static variables that we already have for non-static variables). In this sense, addressing modes is better than separate opcodes.
Since we can't get a contiguous array, maybe we could have a dynamically sized array (or a two-dimensional array if we assume that in the future we might add something else after static variables arrays)? The address already consists of two parts: the index of the nested array in variant_addresses
and the index of the element in the nested array.
The compiler could take into account the depth of class inheritance, instead of storing pointers to scripts. A script that doesn't have a base script has a depth of 0, then 1, 2, 3, and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we could have a dynamically sized array (...) The compiler could take into account the depth of class inheritance, instead of storing pointers to scripts. A script that doesn't have a base script has a depth of 0, then 1, 2, 3, and so on.
Done: dalexeev@992a8b3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about that. Sure it's slower having a two-step approach, but generating this address array dynamically every time seems to be worse for me. Regarding tests, we will need tests for static variables a anyway, so I don't think it makes much of a difference (IMO testing for generating a particular addressing mode is as important as testing for generating opcodes).
fee4ea4
to
e14f4ae
Compare
I reset the branch to the new version as there are more fixes. You can see the old version with separate opcodes here: dalexeev@fee4ea4. |
modules/gdscript/gdscript_vm.cpp
Outdated
#endif | ||
|
||
Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr, script->static_variables.ptrw() }; | ||
Variant **variant_addresses = memnew_arr(Variant *, ADDR_TYPE_STATIC_VAR + script->inheritance_level + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about the preferred way to allocate memory, maybe new[]
or alloca()
is better here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is the preferred way in the Godot codebase:
godot/scene/animation/tween.cpp
Line 748 in 49243a9
const Variant **argptr = (const Variant **)alloca(sizeof(Variant *)); |
e14f4ae
to
d860533
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vnen last reviewed with "Overall it looks good", and the suggestions for improvements have been made, so this should be good to merge and test in the next beta.
I think it's better to wait for George's comment. While this successfully closes linked issues, I'm not sure which approach is better: dedicated opcodes (this option is approved) or addressing modes (I've updated the PR for the second option). And also there is Adam's comment about |
modules/gdscript/gdscript_vm.cpp
Outdated
while (scr) { | ||
#ifdef DEBUG_ENABLED | ||
if (scr->inheritance_level != prev_inheritance_level - 1 || scr->inheritance_level < 0) { | ||
// TODO: error print. | ||
r_err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; | ||
return _get_default_variant_for_data_type(return_type); | ||
} | ||
prev_inheritance_level--; | ||
variant_address_limits[ADDR_TYPE_STATIC_VAR + scr->inheritance_level] = scr->static_variables.size(); | ||
#endif | ||
variant_addresses[ADDR_TYPE_STATIC_VAR + scr->inheritance_level] = scr->static_variables.ptrw(); | ||
scr = scr->_base; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like I said in the other comment, I do feel like doing this every call a bit too much. Usually scripts are not deeply inherited, but if they are this starts to add up, even if they don't use static variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO the previous approach with opcodes was better. The changes in addressing modes is more complex and seems to create extra overhead in all function calls to populate the array.
Okay, I can go back to the opcode version and add some later changes to it. Not sure how to deal with |
7c2ea61
to
c4e836b
Compare
Done. Only the issue with |
c4e836b
to
aebbbda
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks OK to me. The issue with references can be sorted out later since it's shadowed by a bigger issue for now anyway.
Thanks! |
Looks like this is recent enough to cause the 73ac333 linux/clang 16.0.5 compiler error:
|
Thanks after #78465 .patch it compiles again. |
Closes #41919.
Closes #77098.
Closes #77331.