GDScript: Fix some bugs with static variables and functions
This commit is contained in:
parent
598378513b
commit
aebbbda080
21 changed files with 624 additions and 194 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
{
|
||||
HashMap<StringName, Ref<GDScript>>::ConstIterator E = subclasses.find(p_name);
|
||||
if (E) {
|
||||
r_ret = E->value;
|
||||
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, 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;
|
||||
}
|
||||
}
|
||||
top = top->_base;
|
||||
}
|
||||
|
||||
if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) {
|
||||
r_ret = get_source_code();
|
||||
return true;
|
||||
{
|
||||
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, 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;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
top = top->_base;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 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 };
|
||||
|
||||
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, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# GH-77098 p.3
|
||||
|
||||
@static_unload
|
||||
|
||||
@export static var a: int
|
||||
|
||||
func test():
|
||||
pass
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Annotation "@export" cannot be applied to a static variable.
|
|
@ -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)
|
|
@ -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
|
|
@ -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()
|
|
@ -0,0 +1,3 @@
|
|||
GDTEST_OK
|
||||
my_func
|
||||
inner_my_func
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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
|
Loading…
Reference in a new issue