Merge pull request #79880 from dalexeev/gds-fix-id-shadowing-below

GDScript: Fix bug with identifier shadowed below in current scope
This commit is contained in:
Yuri Sizov 2023-07-31 21:01:36 +02:00
commit 3de7dd902c
19 changed files with 367 additions and 213 deletions

View file

@ -225,194 +225,211 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName identifier = in->name;
// Try function parameters.
if (codegen.parameters.has(identifier)) {
return codegen.parameters[identifier];
}
// Try local variables and constants.
if (!p_initializer && codegen.locals.has(identifier)) {
return codegen.locals[identifier];
}
// Try class members.
if (_is_class_member_property(codegen, identifier)) {
// Get property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
gen->write_get_member(temp, identifier);
return temp;
}
// Try members.
if (!codegen.function_node || !codegen.function_node->is_static) {
// Try member variables.
if (codegen.script->member_indices.has(identifier)) {
if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
// Perform getter.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
return temp;
} else {
// No getter or inside getter: direct member access.
int idx = codegen.script->member_indices[identifier].index;
return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
switch (in->source) {
// LOCALS.
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
// Try function parameters.
if (codegen.parameters.has(identifier)) {
return codegen.parameters[identifier];
}
}
}
// Try static variables.
{
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;
// Try local variables and constants.
if (!p_initializer && codegen.locals.has(identifier)) {
return codegen.locals[identifier];
}
} break;
// MEMBERS.
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: {
// Try class members.
if (_is_class_member_property(codegen, identifier)) {
// Get property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
gen->write_get_member(temp, identifier);
return temp;
}
// Try members.
if (!codegen.function_node || !codegen.function_node->is_static) {
// Try member variables.
if (codegen.script->member_indices.has(identifier)) {
if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
// Perform getter.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
return temp;
} else {
// No getter or inside getter: direct member access.
int idx = codegen.script->member_indices[identifier].index;
return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
}
}
}
scr = scr->_base;
}
}
} break;
case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: {
// Try methods and signals (can be Callable and Signal).
// Try class constants.
{
GDScript *owner = codegen.script;
while (owner) {
GDScript *scr = owner;
// Search upwards through parent classes:
const GDScriptParser::ClassNode *base_class = codegen.class_node;
while (base_class != nullptr) {
if (base_class->has_member(identifier)) {
const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
gen->write_get_named(temp, identifier, self);
return temp;
}
}
base_class = base_class->base_type.class_type;
}
// Try in native base.
GDScript *scr = codegen.script;
GDScriptNativeClass *nc = nullptr;
while (scr) {
if (scr->constants.has(identifier)) {
return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
}
if (scr->native.is_valid()) {
nc = scr->native.ptr();
}
scr = scr->_base;
}
// Class C++ integer constant.
if (nc) {
bool success = false;
int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
if (success) {
return codegen.add_constant(constant);
if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
gen->write_get_named(temp, identifier, self);
return temp;
}
} break;
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
case GDScriptParser::IdentifierNode::MEMBER_CLASS: {
// Try class constants.
GDScript *owner = codegen.script;
while (owner) {
GDScript *scr = owner;
GDScriptNativeClass *nc = nullptr;
while (scr) {
if (scr->constants.has(identifier)) {
return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
}
if (scr->native.is_valid()) {
nc = scr->native.ptr();
}
scr = scr->_base;
}
// Class C++ integer constant.
if (nc) {
bool success = false;
int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
if (success) {
return codegen.add_constant(constant);
}
}
owner = owner->_owner;
}
} break;
case GDScriptParser::IdentifierNode::STATIC_VARIABLE: {
// Try static variables.
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;
}
} break;
// GLOBALS.
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: {
// Try globals.
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
// If it's an autoload singleton, we postpone to load it at runtime.
// This is so one autoload doesn't try to load another before it's compiled.
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
gen->write_store_global(global, idx);
return global;
} else {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
return codegen.add_constant(global);
}
}
owner = owner->_owner;
}
}
// Try signals and methods (can be made callables).
{
// Search upwards through parent classes:
const GDScriptParser::ClassNode *base_class = codegen.class_node;
while (base_class != nullptr) {
if (base_class->has_member(identifier)) {
const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
gen->write_get_named(temp, identifier, self);
return temp;
// Try global classes.
if (ScriptServer::is_global_class(identifier)) {
const GDScriptParser::ClassNode *class_node = codegen.class_node;
while (class_node->outer) {
class_node = class_node->outer;
}
}
base_class = base_class->base_type.class_type;
}
// Try in native base.
GDScript *scr = codegen.script;
GDScriptNativeClass *nc = nullptr;
while (scr) {
if (scr->native.is_valid()) {
nc = scr->native.ptr();
}
scr = scr->_base;
}
Ref<Resource> res;
if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
gen->write_get_named(temp, identifier, self);
return temp;
}
}
// Try globals.
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
// If it's an autoload singleton, we postpone to load it at runtime.
// This is so one autoload doesn't try to load another before it's compiled.
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
gen->write_store_global(global, idx);
return global;
} else {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
return codegen.add_constant(global);
}
}
// Try global classes.
if (ScriptServer::is_global_class(identifier)) {
const GDScriptParser::ClassNode *class_node = codegen.class_node;
while (class_node->outer) {
class_node = class_node->outer;
}
Ref<Resource> res;
if (class_node->identifier && class_node->identifier->name == identifier) {
res = Ref<GDScript>(main_script);
} else {
String global_class_path = ScriptServer::get_global_class_path(identifier);
if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
Error err = OK;
res = GDScriptCache::get_full_script(global_class_path, err);
if (err != OK) {
_set_error("Can't load global class " + String(identifier), p_expression);
r_error = ERR_COMPILATION_FAILED;
return GDScriptCodeGenerator::Address();
if (class_node->identifier && class_node->identifier->name == identifier) {
res = Ref<GDScript>(main_script);
} else {
String global_class_path = ScriptServer::get_global_class_path(identifier);
if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
Error err = OK;
res = GDScriptCache::get_full_script(global_class_path, err);
if (err != OK) {
_set_error("Can't load global class " + String(identifier), p_expression);
r_error = ERR_COMPILATION_FAILED;
return GDScriptCodeGenerator::Address();
}
} else {
res = ResourceLoader::load(global_class_path);
if (res.is_null()) {
_set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
r_error = ERR_COMPILATION_FAILED;
return GDScriptCodeGenerator::Address();
}
}
}
} else {
res = ResourceLoader::load(global_class_path);
if (res.is_null()) {
_set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
r_error = ERR_COMPILATION_FAILED;
return GDScriptCodeGenerator::Address();
}
}
}
return codegen.add_constant(res);
}
return codegen.add_constant(res);
}
#ifdef TOOLS_ENABLED
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
gen->write_store_named_global(global, identifier);
return global;
}
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
gen->write_store_named_global(global, identifier);
return global;
}
#endif
} break;
}
// Not found, error.
_set_error("Identifier not found: " + String(identifier), p_expression);
r_error = ERR_COMPILATION_FAILED;