You've already forked godot
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:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user