diff options
Diffstat (limited to 'modules/gdscript/gdscript_byte_codegen.cpp')
-rw-r--r-- | modules/gdscript/gdscript_byte_codegen.cpp | 138 |
1 files changed, 100 insertions, 38 deletions
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 36b157521d..5a50bd8648 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -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); @@ -1192,23 +1196,49 @@ void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_ } void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) { - bool is_validated = false; - MethodBind *method = ClassDB::get_method(p_class, p_method); - if (!is_validated) { - // Perform regular call. - append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1); - for (int i = 0; i < p_arguments.size(); i++) { - append(p_arguments[i]); + // Perform regular call. + append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1); + for (int i = 0; i < p_arguments.size(); i++) { + append(p_arguments[i]); + } + CallTarget ct = get_call_target(p_target); + append(ct.target); + append(method); + append(p_arguments.size()); + ct.cleanup(); + return; +} + +void GDScriptByteCodeGenerator::write_call_native_static_validated(const GDScriptCodeGenerator::Address &p_target, MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) { + Variant::Type return_type = Variant::NIL; + bool has_return = p_method->has_return(); + + if (has_return) { + PropertyInfo return_info = p_method->get_return_info(); + return_type = return_info.type; + } + + CallTarget ct = get_call_target(p_target, return_type); + + if (has_return) { + Variant::Type temp_type = temporaries[ct.target.address].type; + if (temp_type != return_type) { + write_type_adjust(ct.target, return_type); } - CallTarget ct = get_call_target(p_target); - append(ct.target); - append(method); - append(p_arguments.size()); - ct.cleanup(); - return; } + + GDScriptFunction::Opcode code = p_method->has_return() ? GDScriptFunction::OPCODE_CALL_NATIVE_STATIC_VALIDATED_RETURN : GDScriptFunction::OPCODE_CALL_NATIVE_STATIC_VALIDATED_NO_RETURN; + append_opcode_and_argcount(code, 1 + p_arguments.size()); + + for (int i = 0; i < p_arguments.size(); i++) { + append(p_arguments[i]); + } + append(ct.target); + append(p_arguments.size()); + append(p_method); + ct.cleanup(); } void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) { @@ -1579,9 +1609,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,23 +1775,56 @@ 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. + } + } + 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); + } - List<int>::Element *next = E->next(); - E->erase(); - E = next; + 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() { if (!ended && function != nullptr) { memdelete(function); |