summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_byte_codegen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_byte_codegen.cpp')
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp138
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);