diff options
author | Danil Alexeev <danil@alexeev.xyz> | 2023-05-16 13:03:53 +0300 |
---|---|---|
committer | Danil Alexeev <danil@alexeev.xyz> | 2023-06-16 22:52:11 +0300 |
commit | aebbbda08060e0cd130c5a682cd91b6babb18c67 (patch) | |
tree | 169fc0acf33dd216f4247bc02e8bc9e512864ac5 | |
parent | 598378513b256e69e9b824c36136774c41cc763c (diff) | |
download | redot-engine-aebbbda08060e0cd130c5a682cd91b6babb18c67.tar.gz |
GDScript: Fix some bugs with static variables and functions
21 files changed, 614 insertions, 184 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index c3547e3db7..cb70c268b1 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -878,44 +878,55 @@ Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int } bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { - { - const GDScript *top = this; - while (top) { - { - HashMap<StringName, Variant>::ConstIterator E = top->constants.find(p_name); - if (E) { - r_ret = E->value; - return true; - } + if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) { + r_ret = get_source_code(); + return true; + } + + const GDScript *top = this; + while (top) { + { + HashMap<StringName, Variant>::ConstIterator E = top->constants.find(p_name); + if (E) { + r_ret = E->value; + return true; } + } - { - HashMap<StringName, Ref<GDScript>>::ConstIterator E = subclasses.find(p_name); - if (E) { - r_ret = E->value; + { + HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name); + if (E) { + if (E->value.getter) { + Callable::CallError ce; + r_ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce); return true; } + r_ret = top->static_variables[E->value.index]; + return true; } + } - { - HashMap<StringName, MemberInfo>::ConstIterator E = static_variables_indices.find(p_name); - if (E) { - if (E->value.getter) { - Callable::CallError ce; - r_ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce); - return true; - } - r_ret = static_variables[E->value.index]; - return true; + { + HashMap<StringName, GDScriptFunction *>::ConstIterator E = top->member_functions.find(p_name); + if (E && E->value->is_static()) { + if (top->rpc_config.has(p_name)) { + r_ret = Callable(memnew(GDScriptRPCCallable(const_cast<GDScript *>(top), E->key))); + } else { + r_ret = Callable(const_cast<GDScript *>(top), E->key); } + return true; } - top = top->_base; } - if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) { - r_ret = get_source_code(); - return true; + { + HashMap<StringName, Ref<GDScript>>::ConstIterator E = top->subclasses.find(p_name); + if (E) { + r_ret = E->value; + return true; + } } + + top = top->_base; } return false; @@ -925,40 +936,60 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) { if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) { set_source_code(p_value); reload(); - } else { - const GDScript *top = this; - while (top) { - HashMap<StringName, MemberInfo>::ConstIterator E = static_variables_indices.find(p_name); - if (E) { - const GDScript::MemberInfo *member = &E->value; - Variant value = p_value; - if (member->data_type.has_type && !member->data_type.is_type(value)) { - const Variant *args = &p_value; - Callable::CallError err; - Variant::construct(member->data_type.builtin_type, value, &args, 1, err); - if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { - return false; - } - } - if (member->setter) { - const Variant *args = &value; - Callable::CallError err; - callp(member->setter, &args, 1, err); - return err.error == Callable::CallError::CALL_OK; - } else { - static_variables.write[member->index] = value; - return true; + return true; + } + + GDScript *top = this; + while (top) { + HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name); + if (E) { + const MemberInfo *member = &E->value; + Variant value = p_value; + if (member->data_type.has_type && !member->data_type.is_type(value)) { + const Variant *args = &p_value; + Callable::CallError err; + Variant::construct(member->data_type.builtin_type, value, &args, 1, err); + if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { + return false; } } - top = top->_base; + if (member->setter) { + const Variant *args = &value; + Callable::CallError err; + callp(member->setter, &args, 1, err); + return err.error == Callable::CallError::CALL_OK; + } else { + top->static_variables.write[member->index] = value; + return true; + } } + + top = top->_base; } - return true; + return false; } void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const { p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); + + List<PropertyInfo> property_list; + + const GDScript *top = this; + while (top) { + for (const KeyValue<StringName, MemberInfo> &E : top->static_variables_indices) { + PropertyInfo pi = PropertyInfo(E.value.data_type); + pi.name = E.key; + pi.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; // For the script (as a class) it is a non-static property. + property_list.push_back(pi); + } + + top = top->_base; + } + + for (const List<PropertyInfo>::Element *E = property_list.back(); E; E = E->prev()) { + p_properties->push_back(E->get()); + } } void GDScript::_bind_methods() { @@ -1037,6 +1068,16 @@ StringName GDScript::debug_get_member_by_index(int p_idx) const { return "<error>"; } +StringName GDScript::debug_get_static_var_by_index(int p_idx) const { + for (const KeyValue<StringName, MemberInfo> &E : static_variables_indices) { + if (E.value.index == p_idx) { + return E.key; + } + } + + return "<error>"; +} + Ref<GDScript> GDScript::get_base() const { return base; } @@ -1376,8 +1417,8 @@ void GDScript::clear(GDScript::ClearData *p_clear_data) { } clearing = true; - GDScript::ClearData data; - GDScript::ClearData *clear_data = p_clear_data; + ClearData data; + ClearData *clear_data = p_clear_data; bool is_root = false; // If `clear_data` is `nullptr`, it means that it's the root. @@ -1398,12 +1439,12 @@ void GDScript::clear(GDScript::ClearData *p_clear_data) { } member_functions.clear(); - for (KeyValue<StringName, GDScript::MemberInfo> &E : member_indices) { + for (KeyValue<StringName, MemberInfo> &E : member_indices) { clear_data->scripts.insert(E.value.data_type.script_type_ref); E.value.data_type.script_type_ref = Ref<Script>(); } - for (KeyValue<StringName, GDScript::MemberInfo> &E : static_variables_indices) { + for (KeyValue<StringName, MemberInfo> &E : static_variables_indices) { clear_data->scripts.insert(E.value.data_type.script_type_ref); E.value.data_type.script_type_ref = Ref<Script>(); } @@ -1486,7 +1527,6 @@ GDScript::~GDScript() { ////////////////////////////// bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { - //member { HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name); if (E) { @@ -1514,17 +1554,45 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { GDScript *sptr = script.ptr(); while (sptr) { - HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); - if (E) { - Variant name = p_name; - const Variant *args[2] = { &name, &p_value }; + { + HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = sptr->static_variables_indices.find(p_name); + if (E) { + const GDScript::MemberInfo *member = &E->value; + Variant value = p_value; + if (member->data_type.has_type && !member->data_type.is_type(value)) { + const Variant *args = &p_value; + Callable::CallError err; + Variant::construct(member->data_type.builtin_type, value, &args, 1, err); + if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { + return false; + } + } + if (member->setter) { + const Variant *args = &value; + Callable::CallError err; + callp(member->setter, &args, 1, err); + return err.error == Callable::CallError::CALL_OK; + } else { + sptr->static_variables.write[member->index] = value; + return true; + } + } + } - Callable::CallError err; - Variant ret = E->value->call(this, (const Variant **)args, 2, err); - if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) { - return true; + { + HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); + if (E) { + Variant name = p_name; + const Variant *args[2] = { &name, &p_value }; + + Callable::CallError err; + Variant ret = E->value->call(this, (const Variant **)args, 2, err); + if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) { + return true; + } } } + sptr = sptr->_base; } @@ -1532,62 +1600,69 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { } bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { + { + HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name); + if (E) { + if (E->value.getter) { + Callable::CallError err; + r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err); + if (err.error == Callable::CallError::CALL_OK) { + return true; + } + } + r_ret = members[E->value.index]; + return true; + } + } + const GDScript *sptr = script.ptr(); while (sptr) { { - HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name); + HashMap<StringName, Variant>::ConstIterator E = sptr->constants.find(p_name); + if (E) { + r_ret = E->value; + return true; + } + } + + { + HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = sptr->static_variables_indices.find(p_name); if (E) { if (E->value.getter) { - Callable::CallError err; - r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err); - if (err.error == Callable::CallError::CALL_OK) { - return true; - } + Callable::CallError ce; + r_ret = const_cast<GDScript *>(sptr)->callp(E->value.getter, nullptr, 0, ce); + return true; } - r_ret = members[E->value.index]; - return true; //index found + r_ret = sptr->static_variables[E->value.index]; + return true; } } { - const GDScript *sl = sptr; - while (sl) { - HashMap<StringName, Variant>::ConstIterator E = sl->constants.find(p_name); - if (E) { - r_ret = E->value; - return true; //index found - } - sl = sl->_base; + HashMap<StringName, Vector<StringName>>::ConstIterator E = sptr->_signals.find(p_name); + if (E) { + r_ret = Signal(this->owner, E->key); + return true; } } { - // Signals. - const GDScript *sl = sptr; - while (sl) { - HashMap<StringName, Vector<StringName>>::ConstIterator E = sl->_signals.find(p_name); - if (E) { - r_ret = Signal(this->owner, E->key); - return true; //index found + HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_name); + if (E) { + if (sptr->rpc_config.has(p_name)) { + r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key))); + } else { + r_ret = Callable(this->owner, E->key); } - sl = sl->_base; + return true; } } { - // Methods. - const GDScript *sl = sptr; - while (sl) { - HashMap<StringName, GDScriptFunction *>::ConstIterator E = sl->member_functions.find(p_name); - if (E) { - if (sptr->rpc_config.has(p_name)) { - r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key))); - } else { - r_ret = Callable(this->owner, E->key); - } - return true; //index found - } - sl = sl->_base; + HashMap<StringName, Ref<GDScript>>::ConstIterator E = sptr->subclasses.find(p_name); + if (E) { + r_ret = E->value; + return true; } } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 1d6ff6406a..9cf545c41d 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -227,6 +227,7 @@ public: const HashMap<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; } const HashMap<StringName, GDScriptFunction *> &debug_get_member_functions() const; //this is debug only StringName debug_get_member_by_index(int p_idx) const; + StringName debug_get_static_var_by_index(int p_idx) const; Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error); virtual bool can_instantiate() const override; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index e3d3c44dd1..d72b18a95a 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2469,9 +2469,15 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype(); - if (assignee_type.is_constant || (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant)) { + if (assignee_type.is_constant) { push_error("Cannot assign a new value to a constant.", p_assignment->assignee); return; + } else if (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant) { + const GDScriptParser::DataType &base_type = static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->datatype; + if (base_type.kind != GDScriptParser::DataType::SCRIPT && base_type.kind != GDScriptParser::DataType::CLASS) { // Static variables. + push_error("Cannot assign a new value to a constant.", p_assignment->assignee); + return; + } } else if (assignee_type.is_read_only) { push_error("Cannot assign a new value to a read-only property.", p_assignment->assignee); return; @@ -3516,7 +3522,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } break; case GDScriptParser::ClassNode::Member::FUNCTION: { - if (is_base && !base.is_meta_type) { + if (is_base && (!base.is_meta_type || member.function->is_static)) { p_identifier->set_datatype(make_callable_type(member.function->info)); return; } diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 3239f64cb2..47cd3f768b 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -853,6 +853,20 @@ void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const append(p_name); } +void GDScriptByteCodeGenerator::write_set_static_variable(const Address &p_value, const Address &p_class, int p_index) { + append_opcode(GDScriptFunction::OPCODE_SET_STATIC_VARIABLE); + append(p_value); + append(p_class); + append(p_index); +} + +void GDScriptByteCodeGenerator::write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) { + append_opcode(GDScriptFunction::OPCODE_GET_STATIC_VARIABLE); + append(p_target); + append(p_class); + append(p_index); +} + void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) { switch (p_target.type.kind) { case GDScriptDataType::BUILTIN: { diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index bed5cc2405..bbcd252b13 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -365,8 +365,6 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { return p_address.address | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS); case Address::CONSTANT: return p_address.address | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); - case Address::STATIC_VARIABLE: - return p_address.address | (GDScriptFunction::ADDR_TYPE_STATIC_VAR << GDScriptFunction::ADDR_BITS); case Address::LOCAL_VARIABLE: case Address::FUNCTION_PARAMETER: return p_address.address | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); @@ -502,6 +500,8 @@ public: virtual void write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) override; virtual void write_set_member(const Address &p_value, const StringName &p_name) override; virtual void write_get_member(const Address &p_target, const StringName &p_name) override; + virtual void write_set_static_variable(const Address &p_value, const Address &p_class, int p_index) override; + virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) override; virtual void write_assign(const Address &p_target, const Address &p_source) override; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override; virtual void write_assign_true(const Address &p_target) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index fa1732d58f..9810f5395a 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -45,7 +45,6 @@ public: CLASS, MEMBER, CONSTANT, - STATIC_VARIABLE, LOCAL_VARIABLE, FUNCTION_PARAMETER, TEMPORARY, @@ -111,6 +110,8 @@ public: virtual void write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) = 0; virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0; virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0; + virtual void write_set_static_variable(const Address &p_value, const Address &p_class, int p_index) = 0; + virtual void write_get_static_variable(const Address &p_target, const Address &p_class, int p_index) = 0; virtual void write_assign(const Address &p_target, const Address &p_source) = 0; virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0; virtual void write_assign_true(const Address &p_target) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 327e24ef11..88470c30f4 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -262,18 +262,27 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } // Try static variables. - if (codegen.script->static_variables_indices.has(identifier)) { - if (codegen.script->static_variables_indices[identifier].getter != StringName() && codegen.script->static_variables_indices[identifier].getter != codegen.function_name) { - // Perform getter. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->static_variables_indices[identifier].data_type); - GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS); - Vector<GDScriptCodeGenerator::Address> args; // No argument needed. - gen->write_call(temp, class_addr, codegen.script->static_variables_indices[identifier].getter, args); - return temp; - } else { - // No getter or inside getter: direct variable access. - int idx = codegen.script->static_variables_indices[identifier].index; - return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::STATIC_VARIABLE, idx, codegen.script->static_variables_indices[identifier].data_type); + { + GDScript *scr = codegen.script; + while (scr) { + if (scr->static_variables_indices.has(identifier)) { + if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) { + // Perform getter. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type); + GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS); + Vector<GDScriptCodeGenerator::Address> args; // No argument needed. + gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args); + return temp; + } else { + // No getter or inside getter: direct variable access. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type); + GDScriptCodeGenerator::Address _class = codegen.add_constant(scr); + int index = scr->static_variables_indices[identifier].index; + gen->write_get_static_variable(temp, _class, index); + return temp; + } + } + scr = scr->_base; } } @@ -926,6 +935,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code bool member_property_has_setter = false; bool member_property_is_in_setter = false; bool is_static = false; + GDScriptCodeGenerator::Address static_var_class; + int static_var_index = 0; + GDScriptDataType static_var_data_type; + StringName var_name; StringName member_property_setter_function; List<const GDScriptParser::SubscriptNode *> chain; @@ -939,19 +952,39 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Check for a property. if (n->base->type == GDScriptParser::Node::IDENTIFIER) { GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(n->base); - StringName var_name = identifier->name; + var_name = identifier->name; if (_is_class_member_property(codegen, var_name)) { assign_class_member_property = var_name; - } else if (!_is_local_or_parameter(codegen, var_name) && (codegen.script->member_indices.has(var_name) || codegen.script->static_variables_indices.has(var_name))) { - is_member_property = true; - is_static = codegen.script->static_variables_indices.has(var_name); - const GDScript::MemberInfo &minfo = is_static ? codegen.script->static_variables_indices[var_name] : codegen.script->member_indices[var_name]; - 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; - target_member_property.mode = is_static ? GDScriptCodeGenerator::Address::STATIC_VARIABLE : GDScriptCodeGenerator::Address::MEMBER; - target_member_property.address = minfo.index; - target_member_property.type = minfo.data_type; + } else if (!_is_local_or_parameter(codegen, var_name)) { + if (codegen.script->member_indices.has(var_name)) { + is_member_property = true; + is_static = false; + const GDScript::MemberInfo &minfo = codegen.script->member_indices[var_name]; + 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; + target_member_property.mode = GDScriptCodeGenerator::Address::MEMBER; + target_member_property.address = minfo.index; + target_member_property.type = minfo.data_type; + } else { + // Try static variables. + GDScript *scr = codegen.script; + while (scr) { + if (scr->static_variables_indices.has(var_name)) { + is_member_property = true; + is_static = true; + const GDScript::MemberInfo &minfo = scr->static_variables_indices[var_name]; + 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); + static_var_index = minfo.index; + static_var_data_type = minfo.data_type; + break; + } + scr = scr->_base; + } + } } } break; @@ -1104,8 +1137,13 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code if (member_property_has_setter && !member_property_is_in_setter) { Vector<GDScriptCodeGenerator::Address> args; args.push_back(assigned); - GDScriptCodeGenerator::Address self = is_static ? GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS) : GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF); - gen->write_call(GDScriptCodeGenerator::Address(), self, member_property_setter_function, args); + GDScriptCodeGenerator::Address call_base = is_static ? GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS) : GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF); + gen->write_call(GDScriptCodeGenerator::Address(), call_base, member_property_setter_function, args); + } else if (is_static) { + GDScriptCodeGenerator::Address temp = codegen.add_temporary(static_var_data_type); + gen->write_assign(temp, assigned); + gen->write_set_static_variable(temp, static_var_class, static_var_index); + gen->pop_temporary(); } else { gen->write_assign(target_member_property, assigned); } @@ -1155,18 +1193,42 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code bool has_setter = false; bool is_in_setter = false; bool is_static = false; + GDScriptCodeGenerator::Address static_var_class; + int static_var_index = 0; + GDScriptDataType static_var_data_type; + StringName var_name; StringName setter_function; - StringName var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name; - if (!_is_local_or_parameter(codegen, var_name) && (codegen.script->member_indices.has(var_name) || codegen.script->static_variables_indices.has(var_name))) { - is_member = true; - is_static = codegen.script->static_variables_indices.has(var_name); - GDScript::MemberInfo &minfo = is_static ? codegen.script->static_variables_indices[var_name] : codegen.script->member_indices[var_name]; - setter_function = minfo.setter; - has_setter = setter_function != StringName(); - is_in_setter = has_setter && setter_function == codegen.function_name; - member.mode = is_static ? GDScriptCodeGenerator::Address::STATIC_VARIABLE : GDScriptCodeGenerator::Address::MEMBER; - member.address = minfo.index; - member.type = minfo.data_type; + var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name; + if (!_is_local_or_parameter(codegen, var_name)) { + if (codegen.script->member_indices.has(var_name)) { + is_member = true; + is_static = false; + GDScript::MemberInfo &minfo = codegen.script->member_indices[var_name]; + setter_function = minfo.setter; + has_setter = setter_function != StringName(); + is_in_setter = has_setter && setter_function == codegen.function_name; + member.mode = GDScriptCodeGenerator::Address::MEMBER; + member.address = minfo.index; + member.type = minfo.data_type; + } else { + // Try static variables. + GDScript *scr = codegen.script; + while (scr) { + if (scr->static_variables_indices.has(var_name)) { + is_member = true; + is_static = true; + GDScript::MemberInfo &minfo = scr->static_variables_indices[var_name]; + setter_function = minfo.setter; + has_setter = setter_function != StringName(); + is_in_setter = has_setter && setter_function == codegen.function_name; + static_var_class = codegen.add_constant(scr); + static_var_index = minfo.index; + static_var_data_type = minfo.data_type; + break; + } + scr = scr->_base; + } + } } GDScriptCodeGenerator::Address target; @@ -1200,13 +1262,21 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code to_assign = assigned_value; } - GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype(), codegen.script); - if (has_setter && !is_in_setter) { // Call setter. Vector<GDScriptCodeGenerator::Address> args; args.push_back(to_assign); - gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args); + GDScriptCodeGenerator::Address call_base = is_static ? GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS) : GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF); + gen->write_call(GDScriptCodeGenerator::Address(), call_base, setter_function, args); + } else if (is_static) { + GDScriptCodeGenerator::Address temp = codegen.add_temporary(static_var_data_type); + if (assignment->use_conversion_assign) { + gen->write_assign_with_conversion(temp, to_assign); + } else { + gen->write_assign(temp, to_assign); + } + gen->write_set_static_variable(temp, static_var_class, static_var_index); + gen->pop_temporary(); } else { // Just assign. if (assignment->use_conversion_assign) { @@ -2062,11 +2132,11 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ } GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); - GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type); - if (field_type.has_type) { codegen.generator->write_newline(field->start_line); + GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type); + if (field_type.has_container_element_type()) { codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); } else if (field_type.kind == GDScriptDataType::BUILTIN) { @@ -2093,9 +2163,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ continue; } - GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); - - GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type); if (field->initializer) { // Emit proper line change. codegen.generator->write_newline(field->initializer->start_line); @@ -2106,6 +2173,9 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ return nullptr; } + GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); + GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type); + if (field->use_conversion_assign) { codegen.generator->write_assign_with_conversion(dst_address, src_address); } else { @@ -2235,6 +2305,8 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS codegen.is_static = is_static; codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type); + // The static initializer is always called on the same class where the static variables are defined, + // so the CLASS address (current class) can be used instead of `codegen.add_constant(p_script)`. GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS); // Initialize the default values for typed variables before anything. @@ -2251,20 +2323,18 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS } GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); - if (field_type.has_type) { codegen.generator->write_newline(field->start_line); if (field_type.has_container_element_type()) { GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type); codegen.generator->write_construct_typed_array(temp, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - codegen.generator->write_set_named(class_addr, field->identifier->name, temp); + codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index); codegen.generator->pop_temporary(); - } else if (field_type.kind == GDScriptDataType::BUILTIN) { GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type); codegen.generator->write_construct(temp, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); - codegen.generator->write_set_named(class_addr, field->identifier->name, temp); + codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index); codegen.generator->pop_temporary(); } // The `else` branch is for objects, in such case we leave it as `null`. @@ -2281,8 +2351,6 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS continue; } - GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); - if (field->initializer) { // Emit proper line change. codegen.generator->write_newline(field->initializer->start_line); @@ -2293,7 +2361,9 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS return nullptr; } + GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); GDScriptCodeGenerator::Address temp = codegen.add_temporary(field_type); + if (field->use_conversion_assign) { codegen.generator->write_assign_with_conversion(temp, src_address); } else { @@ -2303,7 +2373,7 @@ GDScriptFunction *GDScriptCompiler::_make_static_initializer(Error &r_error, GDS codegen.generator->pop_temporary(); } - codegen.generator->write_set_named(class_addr, field->identifier->name, temp); + codegen.generator->write_set_static_variable(temp, class_addr, p_script->static_variables_indices[field->identifier->name].index); codegen.generator->pop_temporary(); } } diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index cf33f12e3a..0438bd8903 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -312,6 +312,36 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const { incr += 3; } break; + case OPCODE_SET_STATIC_VARIABLE: { + text += "set_static_variable script("; + Ref<GDScript> gdscript = get_constant(_code_ptr[ip + 2] & GDScriptFunction::ADDR_MASK); + text += gdscript.is_valid() ? gdscript->get_fully_qualified_name().get_file() : "<unknown script>"; + text += ")"; + if (gdscript.is_valid()) { + text += "[\"" + gdscript->debug_get_static_var_by_index(_code_ptr[ip + 3]) + "\"]"; + } else { + text += "[<index " + itos(_code_ptr[ip + 3]) + ">]"; + } + text += " = "; + text += DADDR(1); + + incr += 4; + } break; + case OPCODE_GET_STATIC_VARIABLE: { + text += "get_static_variable "; + text += DADDR(1); + text += " = script("; + Ref<GDScript> gdscript = get_constant(_code_ptr[ip + 2] & GDScriptFunction::ADDR_MASK); + text += gdscript.is_valid() ? gdscript->get_fully_qualified_name().get_file() : "<unknown script>"; + text += ")"; + if (gdscript.is_valid()) { + text += "[\"" + gdscript->debug_get_static_var_by_index(_code_ptr[ip + 3]) + "\"]"; + } else { + text += "[<index " + itos(_code_ptr[ip + 3]) + ">]"; + } + + incr += 4; + } break; case OPCODE_ASSIGN: { text += "assign "; text += DADDR(1); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index b6f3e7cc87..9bbfb14f31 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -149,6 +149,7 @@ public: operator PropertyInfo() const { PropertyInfo info; + info.usage = PROPERTY_USAGE_NONE; if (has_type) { switch (kind) { case UNINITIALIZED: @@ -238,6 +239,8 @@ public: OPCODE_GET_NAMED_VALIDATED, OPCODE_SET_MEMBER, OPCODE_GET_MEMBER, + OPCODE_SET_STATIC_VARIABLE, // Only for GDScript. + OPCODE_GET_STATIC_VARIABLE, // Only for GDScript. OPCODE_ASSIGN, OPCODE_ASSIGN_TRUE, OPCODE_ASSIGN_FALSE, @@ -410,14 +413,14 @@ public: ADDR_TYPE_STACK = 0, ADDR_TYPE_CONSTANT = 1, ADDR_TYPE_MEMBER = 2, - ADDR_TYPE_STATIC_VAR = 3, - ADDR_TYPE_MAX = 4, + ADDR_TYPE_MAX = 3, }; enum FixedAddresses { ADDR_STACK_SELF = 0, ADDR_STACK_CLASS = 1, ADDR_STACK_NIL = 2, + FIXED_ADDRESSES_MAX = 3, ADDR_SELF = ADDR_STACK_SELF | (ADDR_TYPE_STACK << ADDR_BITS), ADDR_CLASS = ADDR_STACK_CLASS | (ADDR_TYPE_STACK << ADDR_BITS), ADDR_NIL = ADDR_STACK_NIL | (ADDR_TYPE_STACK << ADDR_BITS), diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index de8a5c66f4..962255f73c 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3827,8 +3827,12 @@ bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node } VariableNode *variable = static_cast<VariableNode *>(p_node); + if (variable->is_static) { + push_error(R"("@onready" annotation cannot be applied to a static variable.)", p_annotation); + return false; + } if (variable->onready) { - push_error(R"("@onready" annotation can only be used once per variable.)"); + push_error(R"("@onready" annotation can only be used once per variable.)", p_annotation); return false; } variable->onready = true; @@ -3841,6 +3845,10 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name)); VariableNode *variable = static_cast<VariableNode *>(p_node); + if (variable->is_static) { + push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation); + return false; + } if (variable->exported) { push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation); return false; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 0ffc025c24..4545689bd9 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -217,6 +217,8 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_GET_NAMED_VALIDATED, \ &&OPCODE_SET_MEMBER, \ &&OPCODE_GET_MEMBER, \ + &&OPCODE_SET_STATIC_VARIABLE, \ + &&OPCODE_GET_STATIC_VARIABLE, \ &&OPCODE_ASSIGN, \ &&OPCODE_ASSIGN_TRUE, \ &&OPCODE_ASSIGN_FALSE, \ @@ -666,7 +668,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Variant *m_v = instruction_args[m_idx] #ifdef DEBUG_ENABLED - uint64_t function_start_time = 0; uint64_t function_call_time = 0; @@ -679,11 +680,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool exit_ok = false; bool awaited = false; #endif + #ifdef DEBUG_ENABLED - int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0, script->static_variables.size() }; + int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0 }; #endif - Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr, script->static_variables.ptrw() }; + Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr }; #ifdef DEBUG_ENABLED OPCODE_WHILE(ip < _code_size) { @@ -1171,6 +1173,42 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_SET_STATIC_VARIABLE) { + CHECK_SPACE(4); + + GET_VARIANT_PTR(value, 0); + + GET_VARIANT_PTR(_class, 1); + GDScript *gdscript = Object::cast_to<GDScript>(_class->operator Object *()); + GD_ERR_BREAK(!gdscript); + + int index = _code_ptr[ip + 3]; + GD_ERR_BREAK(index < 0 || index >= gdscript->static_variables.size()); + + gdscript->static_variables.write[index] = *value; + + ip += 4; + } + DISPATCH_OPCODE; + + OPCODE(OPCODE_GET_STATIC_VARIABLE) { + CHECK_SPACE(4); + + GET_VARIANT_PTR(target, 0); + + GET_VARIANT_PTR(_class, 1); + GDScript *gdscript = Object::cast_to<GDScript>(_class->operator Object *()); + GD_ERR_BREAK(!gdscript); + + int index = _code_ptr[ip + 3]; + GD_ERR_BREAK(index < 0 || index >= gdscript->static_variables.size()); + + *target = gdscript->static_variables[index]; + + ip += 4; + } + DISPATCH_OPCODE; + OPCODE(OPCODE_ASSIGN) { CHECK_SPACE(3); GET_VARIANT_PTR(dst, 0); @@ -3620,7 +3658,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif // Free stack, except reserved addresses. - for (int i = 3; i < _stack_size; i++) { + for (int i = FIXED_ADDRESSES_MAX; i < _stack_size; i++) { stack[i].~Variant(); } #ifdef DEBUG_ENABLED @@ -3628,7 +3666,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif // Always free reserved addresses, since they are never copied. - for (int i = 0; i < 3; i++) { + for (int i = 0; i < FIXED_ADDRESSES_MAX; i++) { stack[i].~Variant(); } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_export_annotation.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_export_annotation.gd new file mode 100644 index 0000000000..66697cbb29 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_export_annotation.gd @@ -0,0 +1,8 @@ +# GH-77098 p.3 + +@static_unload + +@export static var a: int + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_export_annotation.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_export_annotation.out new file mode 100644 index 0000000000..4111aa07af --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_export_annotation.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Annotation "@export" cannot be applied to a static variable. diff --git a/modules/gdscript/tests/scripts/runtime/features/static_access_via_instance.gd b/modules/gdscript/tests/scripts/runtime/features/static_access_via_instance.gd new file mode 100644 index 0000000000..77d01cf00c --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_access_via_instance.gd @@ -0,0 +1,58 @@ +# GH-77098 p.4 + +@static_unload + +class A: + class InnerClass: + pass + + enum NamedEnum { VALUE = 111 } + enum { UNNAMED_ENUM_VALUE = 222 } + const CONSTANT = 333 + static var static_var := 1 + + static func static_func() -> int: + return 444 + +class B extends A: + func test_self(): + print(self.InnerClass is GDScript) + print(self.NamedEnum) + print(self.NamedEnum.VALUE) + print(self.UNNAMED_ENUM_VALUE) + print(self.CONSTANT) + @warning_ignore("static_called_on_instance") + print(self.static_func()) + + prints("test_self before:", self.static_var) + self.static_var = 2 + prints("test_self after:", self.static_var) + +func test(): + var hard := B.new() + hard.test_self() + + print(hard.InnerClass is GDScript) + print(hard.NamedEnum) + print(hard.NamedEnum.VALUE) + print(hard.UNNAMED_ENUM_VALUE) + print(hard.CONSTANT) + @warning_ignore("static_called_on_instance") + print(hard.static_func()) + + prints("hard before:", hard.static_var) + hard.static_var = 3 + prints("hard after:", hard.static_var) + + var weak: Variant = B.new() + print(weak.InnerClass is GDScript) + print(weak.NamedEnum) + print(weak.NamedEnum.VALUE) + print(weak.UNNAMED_ENUM_VALUE) + print(weak.CONSTANT) + @warning_ignore("unsafe_method_access") + print(weak.static_func()) + + prints("weak before:", weak.static_var) + weak.static_var = 4 + prints("weak after:", weak.static_var) diff --git a/modules/gdscript/tests/scripts/runtime/features/static_access_via_instance.out b/modules/gdscript/tests/scripts/runtime/features/static_access_via_instance.out new file mode 100644 index 0000000000..7d7ad04df4 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_access_via_instance.out @@ -0,0 +1,25 @@ +GDTEST_OK +true +{ "VALUE": 111 } +111 +222 +333 +444 +test_self before: 1 +test_self after: 2 +true +{ "VALUE": 111 } +111 +222 +333 +444 +hard before: 2 +hard after: 3 +true +{ "VALUE": 111 } +111 +222 +333 +444 +weak before: 3 +weak after: 4 diff --git a/modules/gdscript/tests/scripts/runtime/features/static_func_as_callable.gd b/modules/gdscript/tests/scripts/runtime/features/static_func_as_callable.gd new file mode 100644 index 0000000000..65635daa36 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_func_as_callable.gd @@ -0,0 +1,17 @@ +# GH-41919 + +class_name TestStaticFuncAsCallable + +class InnerClass: + static func inner_my_func(): + print("inner_my_func") + +static func my_func(): + print("my_func") + +var a: Callable = TestStaticFuncAsCallable.my_func +var b: Callable = InnerClass.inner_my_func + +func test(): + a.call() + b.call() diff --git a/modules/gdscript/tests/scripts/runtime/features/static_func_as_callable.out b/modules/gdscript/tests/scripts/runtime/features/static_func_as_callable.out new file mode 100644 index 0000000000..360bb9f322 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_func_as_callable.out @@ -0,0 +1,3 @@ +GDTEST_OK +my_func +inner_my_func diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables.gd b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd index e193312381..8da8bb7e53 100644 --- a/modules/gdscript/tests/scripts/runtime/features/static_variables.gd +++ b/modules/gdscript/tests/scripts/runtime/features/static_variables.gd @@ -33,24 +33,24 @@ func test(): prints("perm:", perm) prints("prop:", prop) - print("other.perm:", StaticVariablesOther.perm) - print("other.prop:", StaticVariablesOther.prop) + prints("other.perm:", StaticVariablesOther.perm) + prints("other.prop:", StaticVariablesOther.prop) StaticVariablesOther.perm = 2 StaticVariablesOther.prop = "foo" - print("other.perm:", StaticVariablesOther.perm) - print("other.prop:", StaticVariablesOther.prop) + prints("other.perm:", StaticVariablesOther.perm) + prints("other.prop:", StaticVariablesOther.prop) @warning_ignore("unsafe_method_access") var path = get_script().get_path().get_base_dir() - var other = load(path + "/static_variables_load.gd") + var other = load(path + "/static_variables_load.gd") - print("load.perm:", other.perm) - print("load.prop:", other.prop) + prints("load.perm:", other.perm) + prints("load.prop:", other.prop) other.perm = 3 other.prop = "bar" - print("load.perm:", other.perm) - print("load.prop:", other.prop) + prints("load.perm:", other.perm) + prints("load.prop:", other.prop) diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables.out b/modules/gdscript/tests/scripts/runtime/features/static_variables.out index d2491aef5e..650e1d9578 100644 --- a/modules/gdscript/tests/scripts/runtime/features/static_variables.out +++ b/modules/gdscript/tests/scripts/runtime/features/static_variables.out @@ -3,14 +3,14 @@ Inner._static_init inner InnerInner._static_init inner inner data: data perm: 0 -prop: prefix Hello! suffix +prop: Hello! suffix perm: 1 prop: prefix World! suffix -other.perm:0 -other.prop:prefix Hello! suffix -other.perm:2 -other.prop:prefix foo suffix -load.perm:0 -load.prop:prefix Hello! suffix -load.perm:3 -load.prop:prefix bar suffix +other.perm: 0 +other.prop: Hello! suffix +other.perm: 2 +other.prop: prefix foo suffix +load.perm: 0 +load.prop: Hello! suffix +load.perm: 3 +load.prop: prefix bar suffix diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables_2.gd b/modules/gdscript/tests/scripts/runtime/features/static_variables_2.gd new file mode 100644 index 0000000000..7a75d119ed --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_variables_2.gd @@ -0,0 +1,56 @@ +@static_unload + +class A: + static var x: int = 1 + + static var y: int = 42: + set(_value): + print("The setter is NOT called on initialization.") # GH-77098 p.1 + + static func _static_init() -> void: + prints("A _static_init begin:", x) + x = -1 + prints("A _static_init end:", x) + + static func sf(p_x: int) -> void: + x = p_x + prints("sf:", x) + + # GH-77331 + func f(p_x: int) -> void: + x = p_x + prints("f:", x) + +class B extends A: + static func _static_init() -> void: + prints("B _static_init begin:", x) + x = -2 + prints("B _static_init end:", x) + + static func sg(p_x: int) -> void: + x = p_x + prints("sg:", x) + + func g(p_x: int) -> void: + x = p_x + prints("g:", x) + + func h(p_x: int) -> void: + print("h: call f(%d)" % p_x) + f(p_x) + +func test(): + prints(A.x, B.x) + A.x = 1 # GH-77098 p.2 + prints(A.x, B.x) + B.x = 2 + prints(A.x, B.x) + + A.sf(3) + B.sf(4) + B.sg(5) + + var b := B.new() + b.f(6) + b.g(7) + b.h(8) diff --git a/modules/gdscript/tests/scripts/runtime/features/static_variables_2.out b/modules/gdscript/tests/scripts/runtime/features/static_variables_2.out new file mode 100644 index 0000000000..b833911d95 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_variables_2.out @@ -0,0 +1,15 @@ +GDTEST_OK +A _static_init begin: 1 +A _static_init end: -1 +B _static_init begin: -1 +B _static_init end: -2 +-2 -2 +1 1 +2 2 +sf: 3 +sf: 4 +sg: 5 +f: 6 +g: 7 +h: call f(8) +f: 8 |