1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

GDScript: Fix uninitialized local variables not being reset

This commit is contained in:
Danil Alexeev
2024-03-28 21:57:56 +03:00
parent 86415f0245
commit 27d7760f41
10 changed files with 190 additions and 73 deletions

View File

@@ -45,8 +45,8 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool
}
uint32_t GDScriptByteCodeGenerator::add_local(const StringName &p_name, const GDScriptDataType &p_type) {
int stack_pos = locals.size() + RESERVED_STACK;
locals.push_back(StackSlot(p_type.builtin_type));
int stack_pos = locals.size() + GDScriptFunction::FIXED_ADDRESSES_MAX;
locals.push_back(StackSlot(p_type.builtin_type, p_type.can_contain_object()));
add_stack_identifier(p_name, stack_pos);
return stack_pos;
}
@@ -122,7 +122,7 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
List<int> &pool = temporaries_pool[temp_type];
if (pool.is_empty()) {
StackSlot new_temp(temp_type);
StackSlot new_temp(temp_type, p_type.can_contain_object());
int idx = temporaries.size();
pool.push_back(idx);
temporaries.push_back(new_temp);
@@ -136,15 +136,14 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
void GDScriptByteCodeGenerator::pop_temporary() {
ERR_FAIL_COND(used_temporaries.is_empty());
int slot_idx = used_temporaries.back()->get();
const StackSlot &slot = temporaries[slot_idx];
if (slot.type == Variant::NIL) {
if (temporaries[slot_idx].can_contain_object) {
// Avoid keeping in the stack long-lived references to objects,
// which may prevent RefCounted objects from being freed.
// which may prevent `RefCounted` objects from being freed.
// However, the cleanup will be performed an the end of the
// statement, to allow object references to survive chaining.
temporaries_pending_clear.push_back(slot_idx);
temporaries_pending_clear.insert(slot_idx);
}
temporaries_pool[slot.type].push_back(slot_idx);
temporaries_pool[temporaries[slot_idx].type].push_back(slot_idx);
used_temporaries.pop_back();
}
@@ -187,7 +186,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
append_opcode(GDScriptFunction::OPCODE_END);
for (int i = 0; i < temporaries.size(); i++) {
int stack_index = i + max_locals + RESERVED_STACK;
int stack_index = i + max_locals + GDScriptFunction::FIXED_ADDRESSES_MAX;
for (int j = 0; j < temporaries[i].bytecode_indices.size(); j++) {
opcodes.write[temporaries[i].bytecode_indices[j]] = stack_index | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
}
@@ -398,7 +397,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
if (debug_stack) {
function->stack_debug = stack_debug;
}
function->_stack_size = RESERVED_STACK + max_locals + temporaries.size();
function->_stack_size = GDScriptFunction::FIXED_ADDRESSES_MAX + max_locals + temporaries.size();
function->_instruction_args_size = instr_args_max;
#ifdef DEBUG_ENABLED
@@ -945,6 +944,11 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
}
}
void GDScriptByteCodeGenerator::write_assign_null(const Address &p_target) {
append_opcode(GDScriptFunction::OPCODE_ASSIGN_NULL);
append(p_target);
}
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TRUE);
append(p_target);
@@ -1579,9 +1583,8 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_
if (p_use_conversion) {
write_assign_with_conversion(p_variable, temp);
const GDScriptDataType &type = p_variable.type;
if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) {
write_assign_false(temp); // Can contain RefCounted, so clear it.
if (p_variable.type.can_contain_object()) {
clear_address(temp); // Can contain `RefCounted`, so clear it.
}
}
}
@@ -1746,21 +1749,54 @@ void GDScriptByteCodeGenerator::end_block() {
pop_stack_identifiers();
}
void GDScriptByteCodeGenerator::clean_temporaries() {
List<int>::Element *E = temporaries_pending_clear.front();
while (E) {
// The temporary may have been re-used as something else than an object
// since it was added to the list. In that case, there's no need to clear it.
int slot_idx = E->get();
const StackSlot &slot = temporaries[slot_idx];
if (slot.type == Variant::NIL) {
write_assign_false(Address(Address::TEMPORARY, slot_idx));
void GDScriptByteCodeGenerator::clear_temporaries() {
for (int slot_idx : temporaries_pending_clear) {
// The temporary may have been re-used as something else since it was added to the list.
// In that case, there's **no** need to clear it.
if (temporaries[slot_idx].can_contain_object) {
clear_address(Address(Address::TEMPORARY, slot_idx)); // Can contain `RefCounted`, so clear it.
}
List<int>::Element *next = E->next();
E->erase();
E = next;
}
temporaries_pending_clear.clear();
}
void GDScriptByteCodeGenerator::clear_address(const Address &p_address) {
// Do not check `is_local_dirty()` here! Always clear the address since the codegen doesn't track the compiler.
// Also, this method is used to initialize local variables of built-in types, since they cannot be `null`.
if (p_address.type.has_type && p_address.type.kind == GDScriptDataType::BUILTIN) {
switch (p_address.type.builtin_type) {
case Variant::BOOL:
write_assign_false(p_address);
break;
case Variant::ARRAY:
if (p_address.type.has_container_element_type(0)) {
write_construct_typed_array(p_address, p_address.type.get_container_element_type(0), Vector<GDScriptCodeGenerator::Address>());
} else {
write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
}
break;
case Variant::NIL:
case Variant::OBJECT:
write_assign_null(p_address);
break;
default:
write_construct(p_address, p_address.type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
break;
}
} else {
write_assign_null(p_address);
}
if (p_address.mode == Address::LOCAL_VARIABLE) {
dirty_locals.erase(p_address.address);
}
}
// Returns `true` if the local has been re-used and not cleaned up with `clear_address()`.
bool GDScriptByteCodeGenerator::is_local_dirty(const Address &p_address) const {
ERR_FAIL_COND_V(p_address.mode != Address::LOCAL_VARIABLE, false);
return dirty_locals.has(p_address.address);
}
GDScriptByteCodeGenerator::~GDScriptByteCodeGenerator() {